Coverage Report

Created: 2026-10-17 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ada-url/build/singleheader/ada.h
Line
Count
Source
1
/* auto-generated on 2026-10-15 09:03:13 -0400. Do not edit! */
2
/* begin file include/ada.h */
3
/**
4
 * @file ada.h
5
 * @brief Main header for the Ada URL parser library.
6
 *
7
 * This is the primary entry point for the Ada URL parser library. Including
8
 * this single header provides access to the complete Ada API, including:
9
 *
10
 * - URL parsing via `ada::parse()` function
11
 * - Two URL representations: `ada::url` and `ada::url_aggregator`
12
 * - URL search parameters via `ada::url_search_params`
13
 * - URL pattern matching via `ada::url_pattern` (URLPattern API)
14
 * - IDNA (Internationalized Domain Names) support
15
 *
16
 * @example
17
 * ```cpp
18
 *
19
 * // Parse a URL
20
 * auto url = ada::parse("https://example.com/path?query=1");
21
 * if (url) {
22
 *     std::cout << url->get_hostname(); // "example.com"
23
 * }
24
 * ```
25
 *
26
 * @see https://url.spec.whatwg.org/ - WHATWG URL Standard
27
 * @see https://github.com/ada-url/ada - Ada URL Parser GitHub Repository
28
 */
29
#ifndef ADA_H
30
#define ADA_H
31
32
/* begin file include/ada/ada_idna.h */
33
/* auto-generated on 2026-10-09 11:29:39 -0400. Do not edit! */
34
/* begin file include/idna.h */
35
#ifndef ADA_IDNA_H
36
#define ADA_IDNA_H
37
38
/* begin file include/ada/idna/unicode_transcoding.h */
39
#ifndef ADA_IDNA_UNICODE_TRANSCODING_H
40
#define ADA_IDNA_UNICODE_TRANSCODING_H
41
42
#include <string>
43
#include <string_view>
44
45
namespace ada::idna {
46
47
size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output);
48
49
size_t utf8_length_from_utf32(const char32_t* buf, size_t len);
50
51
size_t utf32_length_from_utf8(const char* buf, size_t len);
52
53
size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output);
54
55
}  // namespace ada::idna
56
57
#endif  // ADA_IDNA_UNICODE_TRANSCODING_H
58
/* end file include/ada/idna/unicode_transcoding.h */
59
/* begin file include/ada/idna/mapping.h */
60
#ifndef ADA_IDNA_MAPPING_H
61
#define ADA_IDNA_MAPPING_H
62
63
#include <string>
64
#include <string_view>
65
66
namespace ada::idna {
67
68
// If the input is ascii, then the mapping is just -> lower case.
69
void ascii_map(char* input, size_t length);
70
// Map the characters according to IDNA, returning the empty string on error.
71
std::u32string map(std::u32string_view input);
72
// Map into an existing buffer (cleared on entry). Returns false if any code
73
// point is disallowed. Reusing the buffer avoids repeated heap allocations
74
// when called in a loop over multiple labels.
75
bool map(std::u32string_view input, std::u32string& out);
76
77
}  // namespace ada::idna
78
79
#endif
80
/* end file include/ada/idna/mapping.h */
81
/* begin file include/ada/idna/normalization.h */
82
#ifndef ADA_IDNA_NORMALIZATION_H
83
#define ADA_IDNA_NORMALIZATION_H
84
85
#include <string>
86
#include <string_view>
87
88
namespace ada::idna {
89
90
// Returns true if `input` is already in Unicode Normalization Form C.
91
// Requires that internal tables have been loaded (call ensure via normalize
92
// or map first, or this returns false if tables are unavailable).
93
[[nodiscard]] bool is_already_nfc(std::u32string_view input) noexcept;
94
95
// Normalize the characters according to IDNA (Unicode Normalization Form C).
96
// Returns false if the internal Unicode tables could not be loaded; in that
97
// case `input` is left unchanged. Skips work when the string is already NFC.
98
[[nodiscard]] bool normalize(std::u32string& input);
99
100
}  // namespace ada::idna
101
#endif
102
/* end file include/ada/idna/normalization.h */
103
/* begin file include/ada/idna/punycode.h */
104
#ifndef ADA_IDNA_PUNYCODE_H
105
#define ADA_IDNA_PUNYCODE_H
106
107
#include <string>
108
#include <string_view>
109
110
namespace ada::idna {
111
112
bool punycode_to_utf32(std::string_view input, std::u32string& out);
113
bool verify_punycode(std::string_view input);
114
bool utf32_to_punycode(std::u32string_view input, std::string& out);
115
116
}  // namespace ada::idna
117
118
#endif  // ADA_IDNA_PUNYCODE_H
119
/* end file include/ada/idna/punycode.h */
120
/* begin file include/ada/idna/validity.h */
121
#ifndef ADA_IDNA_VALIDITY_H
122
#define ADA_IDNA_VALIDITY_H
123
124
#include <string>
125
#include <string_view>
126
127
namespace ada::idna {
128
129
/**
130
 * @see https://www.unicode.org/reports/tr46/#Validity_Criteria
131
 */
132
bool is_label_valid(std::u32string_view label);
133
134
}  // namespace ada::idna
135
136
#endif  // ADA_IDNA_VALIDITY_H
137
/* end file include/ada/idna/validity.h */
138
/* begin file include/ada/idna/to_ascii.h */
139
#ifndef ADA_IDNA_TO_ASCII_H
140
#define ADA_IDNA_TO_ASCII_H
141
142
#include <string>
143
#include <string_view>
144
145
/* begin file include/ada/idna/limits.h */
146
#ifndef ADA_IDNA_LIMITS_H
147
#define ADA_IDNA_LIMITS_H
148
149
#include <cstddef>
150
151
namespace ada::idna {
152
153
// Maximum accepted UTF-8 domain length for non-ASCII to_ascii input and for
154
// to_unicode input.
155
// Bounds heap growth under untrusted input (DoS resistance). DNS wire limits
156
// are smaller; this allows long Unicode labels used in URL tests/fixtures.
157
inline constexpr size_t max_domain_input_bytes = 16384;
158
159
}  // namespace ada::idna
160
161
#endif  // ADA_IDNA_LIMITS_H
162
/* end file include/ada/idna/limits.h */
163
164
namespace ada::idna {
165
166
// Converts a domain (e.g., www.google.com) possibly containing international
167
// characters to an ascii domain (with punycode). It will not do percent
168
// decoding: percent decoding should be done prior to calling this function. We
169
// do not remove tabs and spaces, they should have been removed prior to calling
170
// this function. We also do not trim control characters. We also assume that
171
// the input is not empty. We return "" on error. Non-ASCII inputs longer than
172
// max_domain_input_bytes are rejected; ASCII inputs of any length are accepted
173
// (they are only lowercased).
174
//
175
// This function may accept or even produce invalid domains (WHATWG carve-outs).
176
std::string to_ascii(std::string_view ut8_string);
177
178
// Same as to_ascii, but writes into `out` and returns false on error without
179
// relying on empty-string ambiguity.
180
[[nodiscard]] bool to_ascii(std::string_view ut8_string, std::string& out);
181
182
// Returns true if the string contains a forbidden code point according to the
183
// WHATGL URL specification:
184
// https://url.spec.whatwg.org/#forbidden-domain-code-point
185
bool contains_forbidden_domain_code_point(std::string_view ascii_string);
186
187
bool constexpr is_ascii(std::u32string_view view);
188
bool constexpr is_ascii(std::string_view view);
189
190
}  // namespace ada::idna
191
192
#endif  // ADA_IDNA_TO_ASCII_H
193
/* end file include/ada/idna/to_ascii.h */
194
/* begin file include/ada/idna/to_unicode.h */
195
#ifndef ADA_IDNA_TO_UNICODE_H
196
#define ADA_IDNA_TO_UNICODE_H
197
198
#include <string>
199
#include <string_view>
200
201
namespace ada::idna {
202
203
// UTS #46 ToUnicode. Never fails per the standard: on step failure the original
204
// label is kept. Inputs longer than max_domain_input_bytes are returned
205
// unchanged as a safety measure under untrusted input.
206
std::string to_unicode(std::string_view input);
207
208
// Writes into `out`. Returns false only if the input exceeds
209
// max_domain_input_bytes (out is left empty). Otherwise always returns true
210
// (ToUnicode does not fail).
211
[[nodiscard]] bool to_unicode(std::string_view input, std::string& out);
212
213
}  // namespace ada::idna
214
215
#endif  // ADA_IDNA_TO_UNICODE_H
216
/* end file include/ada/idna/to_unicode.h */
217
/* begin file include/ada/idna/identifier.h */
218
#ifndef ADA_IDNA_IDENTIFIER_H
219
#define ADA_IDNA_IDENTIFIER_H
220
221
#include <string>
222
#include <string_view>
223
224
namespace ada::idna {
225
226
// Verify if it is valid name code point given a Unicode code point and a
227
// boolean first: If first is true return the result of checking if code point
228
// is contained in the IdentifierStart set of code points. Otherwise return the
229
// result of checking if code point is contained in the IdentifierPart set of
230
// code points. Returns false if the input is empty or the code point is not
231
// valid. There is minimal Unicode error handling: the input should be valid
232
// UTF-8. https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point
233
bool valid_name_code_point(char32_t code_point, bool first);
234
235
}  // namespace ada::idna
236
237
#endif
238
/* end file include/ada/idna/identifier.h */
239
240
#endif
241
/* end file include/idna.h */
242
/* end file include/ada/ada_idna.h */
243
/* begin file include/ada/character_sets.h */
244
/**
245
 * @file character_sets.h
246
 * @brief Declaration of the character sets used by unicode functions.
247
 * @author Node.js
248
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
249
 */
250
#ifndef ADA_CHARACTER_SETS_H
251
#define ADA_CHARACTER_SETS_H
252
253
/* begin file include/ada/common_defs.h */
254
/**
255
 * @file common_defs.h
256
 * @brief Cross-platform compiler macros and common definitions.
257
 *
258
 * This header provides compiler-specific macros for optimization hints,
259
 * platform detection, SIMD support detection, and development/debug utilities.
260
 * It ensures consistent behavior across different compilers (GCC, Clang, MSVC).
261
 */
262
#ifndef ADA_COMMON_DEFS_H
263
#define ADA_COMMON_DEFS_H
264
265
// https://en.cppreference.com/w/cpp/feature_test#Library_features
266
// detect C++20 features
267
#include <version>
268
269
#ifdef _MSC_VER
270
#define ADA_VISUAL_STUDIO 1
271
/**
272
 * We want to differentiate carefully between
273
 * clang under visual studio and regular visual
274
 * studio.
275
 */
276
#ifdef __clang__
277
// clang under visual studio
278
#define ADA_CLANG_VISUAL_STUDIO 1
279
#else
280
// just regular visual studio (best guess)
281
#define ADA_REGULAR_VISUAL_STUDIO 1
282
#endif  // __clang__
283
#endif  // _MSC_VER
284
285
#if defined(__GNUC__)
286
// Marks a block with a name so that MCA analysis can see it.
287
#define ADA_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
288
#define ADA_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
289
#define ADA_DEBUG_BLOCK(name, block) \
290
  BEGIN_DEBUG_BLOCK(name);           \
291
  block;                             \
292
  END_DEBUG_BLOCK(name);
293
#else
294
#define ADA_BEGIN_DEBUG_BLOCK(name)
295
#define ADA_END_DEBUG_BLOCK(name)
296
#define ADA_DEBUG_BLOCK(name, block)
297
#endif
298
299
// Align to N-byte boundary
300
#define ADA_ROUNDUP_N(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
301
#define ADA_ROUNDDOWN_N(a, n) ((a) & ~((n) - 1))
302
303
#define ADA_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n) - 1)) == 0)
304
305
#if defined(ADA_REGULAR_VISUAL_STUDIO)
306
307
#define ada_really_inline __forceinline
308
#define ada_never_inline __declspec(noinline)
309
310
#define ada_unused
311
#define ada_warn_unused
312
313
#define ADA_PUSH_DISABLE_WARNINGS __pragma(warning(push))
314
#define ADA_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0))
315
#define ADA_DISABLE_VS_WARNING(WARNING_NUMBER) \
316
  __pragma(warning(disable : WARNING_NUMBER))
317
// Get rid of Intellisense-only warnings (Code Analysis)
318
// Though __has_include is C++17, it is supported in Visual Studio 2017 or
319
// better (_MSC_VER>=1910).
320
#ifdef __has_include
321
#if __has_include(<CppCoreCheck\Warnings.h>)
322
#include <CppCoreCheck\Warnings.h>
323
#define ADA_DISABLE_UNDESIRED_WARNINGS \
324
  ADA_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
325
#endif
326
#endif
327
328
#ifndef ADA_DISABLE_UNDESIRED_WARNINGS
329
#define ADA_DISABLE_UNDESIRED_WARNINGS
330
#endif
331
332
#define ADA_DISABLE_DEPRECATED_WARNING ADA_DISABLE_VS_WARNING(4996)
333
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING
334
#define ADA_POP_DISABLE_WARNINGS __pragma(warning(pop))
335
336
#else  // ADA_REGULAR_VISUAL_STUDIO
337
338
#define ada_really_inline inline __attribute__((always_inline))
339
#define ada_never_inline inline __attribute__((noinline))
340
341
#define ada_unused __attribute__((unused))
342
#define ada_warn_unused __attribute__((warn_unused_result))
343
344
#define ADA_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
345
// gcc doesn't seem to disable all warnings with all and extra, add warnings
346
// here as necessary
347
#define ADA_PUSH_DISABLE_ALL_WARNINGS               \
348
  ADA_PUSH_DISABLE_WARNINGS                         \
349
  ADA_DISABLE_GCC_WARNING("-Weffc++")               \
350
  ADA_DISABLE_GCC_WARNING("-Wall")                  \
351
  ADA_DISABLE_GCC_WARNING("-Wconversion")           \
352
  ADA_DISABLE_GCC_WARNING("-Wextra")                \
353
  ADA_DISABLE_GCC_WARNING("-Wattributes")           \
354
  ADA_DISABLE_GCC_WARNING("-Wimplicit-fallthrough") \
355
  ADA_DISABLE_GCC_WARNING("-Wnon-virtual-dtor")     \
356
  ADA_DISABLE_GCC_WARNING("-Wreturn-type")          \
357
  ADA_DISABLE_GCC_WARNING("-Wshadow")               \
358
  ADA_DISABLE_GCC_WARNING("-Wunused-parameter")     \
359
  ADA_DISABLE_GCC_WARNING("-Wunused-variable")      \
360
  ADA_DISABLE_GCC_WARNING("-Wsign-compare")
361
#define ADA_PRAGMA(P) _Pragma(#P)
362
#define ADA_DISABLE_GCC_WARNING(WARNING) \
363
  ADA_PRAGMA(GCC diagnostic ignored WARNING)
364
#if defined(ADA_CLANG_VISUAL_STUDIO)
365
#define ADA_DISABLE_UNDESIRED_WARNINGS \
366
  ADA_DISABLE_GCC_WARNING("-Wmicrosoft-include")
367
#else
368
#define ADA_DISABLE_UNDESIRED_WARNINGS
369
#endif
370
#define ADA_DISABLE_DEPRECATED_WARNING \
371
  ADA_DISABLE_GCC_WARNING("-Wdeprecated-declarations")
372
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING \
373
  ADA_DISABLE_GCC_WARNING("-Wstrict-overflow")
374
#define ADA_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
375
376
#endif  // MSC_VER
377
378
#if defined(ADA_VISUAL_STUDIO)
379
/**
380
 * It does not matter here whether you are using
381
 * the regular visual studio or clang under visual
382
 * studio.
383
 */
384
#if ADA_USING_LIBRARY
385
#define ADA_DLLIMPORTEXPORT __declspec(dllimport)
386
#else
387
#define ADA_DLLIMPORTEXPORT __declspec(dllexport)
388
#endif
389
#else
390
#define ADA_DLLIMPORTEXPORT
391
#endif
392
393
/// If EXPR is an error, returns it.
394
#define ADA_TRY(EXPR)   \
395
  {                     \
396
    auto _err = (EXPR); \
397
    if (_err) {         \
398
      return _err;      \
399
    }                   \
400
  }
401
402
// __has_cpp_attribute is part of C++20
403
#if !defined(__has_cpp_attribute)
404
#define __has_cpp_attribute(x) 0
405
#endif
406
407
#if __has_cpp_attribute(gnu::noinline)
408
#define ADA_ATTRIBUTE_NOINLINE [[gnu::noinline]]
409
#else
410
#define ADA_ATTRIBUTE_NOINLINE
411
#endif
412
413
namespace ada {
414
0
[[noreturn]] inline void unreachable() {
415
0
#ifdef __GNUC__
416
0
  __builtin_unreachable();
417
#elif defined(_MSC_VER)
418
  __assume(false);
419
#else
420
#endif
421
0
}
422
}  // namespace ada
423
424
// Unless the programmer has already set ADA_DEVELOPMENT_CHECKS,
425
// we want to set it under debug builds. We detect a debug build
426
// under Visual Studio when the _DEBUG macro is set. Under the other
427
// compilers, we use the fact that they define __OPTIMIZE__ whenever
428
// they allow optimizations.
429
// It is possible that this could miss some cases where ADA_DEVELOPMENT_CHECKS
430
// is helpful, but the programmer can set the macro ADA_DEVELOPMENT_CHECKS.
431
// It could also wrongly set ADA_DEVELOPMENT_CHECKS (e.g., if the programmer
432
// sets _DEBUG in a release build under Visual Studio, or if some compiler fails
433
// to set the __OPTIMIZE__ macro).
434
#if !defined(ADA_DEVELOPMENT_CHECKS) && !defined(NDEBUG)
435
#ifdef _MSC_VER
436
// Visual Studio seems to set _DEBUG for debug builds.
437
#ifdef _DEBUG
438
#define ADA_DEVELOPMENT_CHECKS 1
439
#endif  // _DEBUG
440
#else   // _MSC_VER
441
// All other compilers appear to set __OPTIMIZE__ to a positive integer
442
// when the compiler is optimizing.
443
#ifndef __OPTIMIZE__
444
#define ADA_DEVELOPMENT_CHECKS 1
445
#endif  // __OPTIMIZE__
446
#endif  // _MSC_VER
447
#endif  // ADA_DEVELOPMENT_CHECKS
448
449
#define ADA_STR(x) #x
450
451
#if ADA_DEVELOPMENT_CHECKS
452
#define ADA_REQUIRE(EXPR) \
453
  {                       \
454
    if (!(EXPR) { abort(); }) }
455
456
#define ADA_FAIL(MESSAGE)                            \
457
  do {                                               \
458
    std::cerr << "FAIL: " << (MESSAGE) << std::endl; \
459
    abort();                                         \
460
  } while (0);
461
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)                                    \
462
  do {                                                                         \
463
    if (LHS != RHS) {                                                          \
464
      std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \
465
      ADA_FAIL(MESSAGE);                                                       \
466
    }                                                                          \
467
  } while (0);
468
#define ADA_ASSERT_TRUE(COND)                                               \
469
  do {                                                                      \
470
    if (!(COND)) {                                                          \
471
      std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \
472
                << std::endl;                                               \
473
      ADA_FAIL(ADA_STR(COND));                                              \
474
    }                                                                       \
475
  } while (0);
476
#else
477
#define ADA_FAIL(MESSAGE)
478
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)
479
#define ADA_ASSERT_TRUE(COND)
480
#endif
481
482
#ifdef ADA_VISUAL_STUDIO
483
#define ADA_ASSUME(COND) __assume(COND)
484
#else
485
#define ADA_ASSUME(COND)       \
486
  do {                         \
487
    if (!(COND)) {             \
488
      __builtin_unreachable(); \
489
    }                          \
490
  } while (0)
491
#endif
492
493
#if defined(__SSSE3__)
494
#define ADA_SSSE3 1
495
#endif
496
497
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
498
    (defined(_M_AMD64) || defined(_M_X64) ||                         \
499
     (defined(_M_IX86_FP) && _M_IX86_FP == 2))
500
#define ADA_SSE2 1
501
#endif
502
503
// AVX-512 byte/word ops + 128/256-bit vectors of AVX-512 instructions.
504
// Used for optional high-performance IP address parsing kernels.
505
#if defined(__AVX512BW__) && defined(__AVX512VL__)
506
#define ADA_AVX512 1
507
#endif
508
509
#if defined(__aarch64__) || defined(_M_ARM64)
510
#define ADA_NEON 1
511
#endif
512
513
#if defined(__loongarch_sx)
514
#define ADA_LSX 1
515
#endif
516
517
#if defined(__riscv_v) && __riscv_v_intrinsic >= 11000
518
// Support RVV intrinsics v0.11 and above
519
#define ADA_RVV 1
520
#endif
521
522
#ifndef __has_cpp_attribute
523
#define ada_lifetime_bound
524
#elif __has_cpp_attribute(msvc::lifetimebound)
525
#define ada_lifetime_bound [[msvc::lifetimebound]]
526
#elif __has_cpp_attribute(clang::lifetimebound)
527
#define ada_lifetime_bound [[clang::lifetimebound]]
528
#elif __has_cpp_attribute(lifetimebound)
529
#define ada_lifetime_bound [[lifetimebound]]
530
#else
531
#define ada_lifetime_bound
532
#endif
533
534
#ifdef __cpp_lib_format
535
#if __cpp_lib_format >= 202110L
536
#include <format>
537
#define ADA_HAS_FORMAT 1
538
#endif
539
#endif
540
541
#ifndef ADA_INCLUDE_URL_PATTERN
542
#define ADA_INCLUDE_URL_PATTERN 1
543
#endif  // ADA_INCLUDE_URL_PATTERN
544
545
// When set, NEON all-zero tests use an integer compare instead of
546
// reinterpreting the bits as a double. The double compare is faster (no
547
// GPR transfer) but is incorrect if flush-to-zero is enabled.
548
#ifndef ADA_NEON_SAFE_ZERO_CHECK
549
#define ADA_NEON_SAFE_ZERO_CHECK 1
550
#endif  // ADA_NEON_SAFE_ZERO_CHECK
551
552
#endif  // ADA_COMMON_DEFS_H
553
/* end file include/ada/common_defs.h */
554
#include <cstdint>
555
556
/**
557
 * These functions are not part of our public API and may
558
 * change at any time.
559
 * @private
560
 * @namespace ada::character_sets
561
 * @brief Includes the definitions for unicode character sets.
562
 */
563
namespace ada::character_sets {
564
ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i);
565
}  // namespace ada::character_sets
566
567
#endif  // ADA_CHARACTER_SETS_H
568
/* end file include/ada/character_sets.h */
569
/* begin file include/ada/character_sets-inl.h */
570
/**
571
 * @file character_sets-inl.h
572
 * @brief Definitions of the character sets used by unicode functions.
573
 * @author Node.js
574
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
575
 */
576
#ifndef ADA_CHARACTER_SETS_INL_H
577
#define ADA_CHARACTER_SETS_INL_H
578
579
580
#include <cstdint>
581
582
/**
583
 * These functions are not part of our public API and may
584
 * change at any time.
585
 * @private
586
 */
587
namespace ada::character_sets {
588
589
constexpr char hex[1024] =
590
    "%00\0%01\0%02\0%03\0%04\0%05\0%06\0%07\0"
591
    "%08\0%09\0%0A\0%0B\0%0C\0%0D\0%0E\0%0F\0"
592
    "%10\0%11\0%12\0%13\0%14\0%15\0%16\0%17\0"
593
    "%18\0%19\0%1A\0%1B\0%1C\0%1D\0%1E\0%1F\0"
594
    "%20\0%21\0%22\0%23\0%24\0%25\0%26\0%27\0"
595
    "%28\0%29\0%2A\0%2B\0%2C\0%2D\0%2E\0%2F\0"
596
    "%30\0%31\0%32\0%33\0%34\0%35\0%36\0%37\0"
597
    "%38\0%39\0%3A\0%3B\0%3C\0%3D\0%3E\0%3F\0"
598
    "%40\0%41\0%42\0%43\0%44\0%45\0%46\0%47\0"
599
    "%48\0%49\0%4A\0%4B\0%4C\0%4D\0%4E\0%4F\0"
600
    "%50\0%51\0%52\0%53\0%54\0%55\0%56\0%57\0"
601
    "%58\0%59\0%5A\0%5B\0%5C\0%5D\0%5E\0%5F\0"
602
    "%60\0%61\0%62\0%63\0%64\0%65\0%66\0%67\0"
603
    "%68\0%69\0%6A\0%6B\0%6C\0%6D\0%6E\0%6F\0"
604
    "%70\0%71\0%72\0%73\0%74\0%75\0%76\0%77\0"
605
    "%78\0%79\0%7A\0%7B\0%7C\0%7D\0%7E\0%7F\0"
606
    "%80\0%81\0%82\0%83\0%84\0%85\0%86\0%87\0"
607
    "%88\0%89\0%8A\0%8B\0%8C\0%8D\0%8E\0%8F\0"
608
    "%90\0%91\0%92\0%93\0%94\0%95\0%96\0%97\0"
609
    "%98\0%99\0%9A\0%9B\0%9C\0%9D\0%9E\0%9F\0"
610
    "%A0\0%A1\0%A2\0%A3\0%A4\0%A5\0%A6\0%A7\0"
611
    "%A8\0%A9\0%AA\0%AB\0%AC\0%AD\0%AE\0%AF\0"
612
    "%B0\0%B1\0%B2\0%B3\0%B4\0%B5\0%B6\0%B7\0"
613
    "%B8\0%B9\0%BA\0%BB\0%BC\0%BD\0%BE\0%BF\0"
614
    "%C0\0%C1\0%C2\0%C3\0%C4\0%C5\0%C6\0%C7\0"
615
    "%C8\0%C9\0%CA\0%CB\0%CC\0%CD\0%CE\0%CF\0"
616
    "%D0\0%D1\0%D2\0%D3\0%D4\0%D5\0%D6\0%D7\0"
617
    "%D8\0%D9\0%DA\0%DB\0%DC\0%DD\0%DE\0%DF\0"
618
    "%E0\0%E1\0%E2\0%E3\0%E4\0%E5\0%E6\0%E7\0"
619
    "%E8\0%E9\0%EA\0%EB\0%EC\0%ED\0%EE\0%EF\0"
620
    "%F0\0%F1\0%F2\0%F3\0%F4\0%F5\0%F6\0%F7\0"
621
    "%F8\0%F9\0%FA\0%FB\0%FC\0%FD\0%FE\0%FF";
622
623
constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32] = {
624
    // 00     01     02     03     04     05     06     07
625
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
626
    // 08     09     0A     0B     0C     0D     0E     0F
627
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
628
    // 10     11     12     13     14     15     16     17
629
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
630
    // 18     19     1A     1B     1C     1D     1E     1F
631
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
632
    // 20     21     22     23     24     25     26     27
633
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
634
    // 28     29     2A     2B     2C     2D     2E     2F
635
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
636
    // 30     31     32     33     34     35     36     37
637
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
638
    // 38     39     3A     3B     3C     3D     3E     3F
639
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
640
    // 40     41     42     43     44     45     46     47
641
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
642
    // 48     49     4A     4B     4C     4D     4E     4F
643
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
644
    // 50     51     52     53     54     55     56     57
645
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
646
    // 58     59     5A     5B     5C     5D     5E     5F
647
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
648
    // 60     61     62     63     64     65     66     67
649
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
650
    // 68     69     6A     6B     6C     6D     6E     6F
651
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
652
    // 70     71     72     73     74     75     76     77
653
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
654
    // 78     79     7A     7B     7C     7D     7E     7F
655
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
656
    // 80     81     82     83     84     85     86     87
657
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
658
    // 88     89     8A     8B     8C     8D     8E     8F
659
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
660
    // 90     91     92     93     94     95     96     97
661
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
662
    // 98     99     9A     9B     9C     9D     9E     9F
663
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
664
    // A0     A1     A2     A3     A4     A5     A6     A7
665
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
666
    // A8     A9     AA     AB     AC     AD     AE     AF
667
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
668
    // B0     B1     B2     B3     B4     B5     B6     B7
669
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
670
    // B8     B9     BA     BB     BC     BD     BE     BF
671
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
672
    // C0     C1     C2     C3     C4     C5     C6     C7
673
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
674
    // C8     C9     CA     CB     CC     CD     CE     CF
675
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
676
    // D0     D1     D2     D3     D4     D5     D6     D7
677
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
678
    // D8     D9     DA     DB     DC     DD     DE     DF
679
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
680
    // E0     E1     E2     E3     E4     E5     E6     E7
681
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
682
    // E8     E9     EA     EB     EC     ED     EE     EF
683
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
684
    // F0     F1     F2     F3     F4     F5     F6     F7
685
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
686
    // F8     F9     FA     FB     FC     FD     FE     FF
687
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
688
689
constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32] = {
690
    // 00     01     02     03     04     05     06     07
691
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
692
    // 08     09     0A     0B     0C     0D     0E     0F
693
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
694
    // 10     11     12     13     14     15     16     17
695
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
696
    // 18     19     1A     1B     1C     1D     1E     1F
697
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
698
    // 20     21     22     23     24     25     26     27
699
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x80,
700
    // 28     29     2A     2B     2C     2D     2E     2F
701
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
702
    // 30     31     32     33     34     35     36     37
703
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
704
    // 38     39     3A     3B     3C     3D     3E     3F
705
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
706
    // 40     41     42     43     44     45     46     47
707
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
708
    // 48     49     4A     4B     4C     4D     4E     4F
709
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
710
    // 50     51     52     53     54     55     56     57
711
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
712
    // 58     59     5A     5B     5C     5D     5E     5F
713
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
714
    // 60     61     62     63     64     65     66     67
715
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
716
    // 68     69     6A     6B     6C     6D     6E     6F
717
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
718
    // 70     71     72     73     74     75     76     77
719
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
720
    // 78     79     7A     7B     7C     7D     7E     7F
721
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
722
    // 80     81     82     83     84     85     86     87
723
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
724
    // 88     89     8A     8B     8C     8D     8E     8F
725
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
726
    // 90     91     92     93     94     95     96     97
727
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
728
    // 98     99     9A     9B     9C     9D     9E     9F
729
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
730
    // A0     A1     A2     A3     A4     A5     A6     A7
731
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
732
    // A8     A9     AA     AB     AC     AD     AE     AF
733
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
734
    // B0     B1     B2     B3     B4     B5     B6     B7
735
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
736
    // B8     B9     BA     BB     BC     BD     BE     BF
737
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
738
    // C0     C1     C2     C3     C4     C5     C6     C7
739
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
740
    // C8     C9     CA     CB     CC     CD     CE     CF
741
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
742
    // D0     D1     D2     D3     D4     D5     D6     D7
743
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
744
    // D8     D9     DA     DB     DC     DD     DE     DF
745
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
746
    // E0     E1     E2     E3     E4     E5     E6     E7
747
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
748
    // E8     E9     EA     EB     EC     ED     EE     EF
749
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
750
    // F0     F1     F2     F3     F4     F5     F6     F7
751
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
752
    // F8     F9     FA     FB     FC     FD     FE     FF
753
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
754
755
constexpr uint8_t QUERY_PERCENT_ENCODE[32] = {
756
    // 00     01     02     03     04     05     06     07
757
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
758
    // 08     09     0A     0B     0C     0D     0E     0F
759
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
760
    // 10     11     12     13     14     15     16     17
761
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
762
    // 18     19     1A     1B     1C     1D     1E     1F
763
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
764
    // 20     21     22     23     24     25     26     27
765
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
766
    // 28     29     2A     2B     2C     2D     2E     2F
767
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
768
    // 30     31     32     33     34     35     36     37
769
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
770
    // 38     39     3A     3B     3C     3D     3E     3F
771
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
772
    // 40     41     42     43     44     45     46     47
773
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
774
    // 48     49     4A     4B     4C     4D     4E     4F
775
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
776
    // 50     51     52     53     54     55     56     57
777
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
778
    // 58     59     5A     5B     5C     5D     5E     5F
779
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
780
    // 60     61     62     63     64     65     66     67
781
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
782
    // 68     69     6A     6B     6C     6D     6E     6F
783
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
784
    // 70     71     72     73     74     75     76     77
785
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
786
    // 78     79     7A     7B     7C     7D     7E     7F
787
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
788
    // 80     81     82     83     84     85     86     87
789
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
790
    // 88     89     8A     8B     8C     8D     8E     8F
791
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
792
    // 90     91     92     93     94     95     96     97
793
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
794
    // 98     99     9A     9B     9C     9D     9E     9F
795
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
796
    // A0     A1     A2     A3     A4     A5     A6     A7
797
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
798
    // A8     A9     AA     AB     AC     AD     AE     AF
799
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
800
    // B0     B1     B2     B3     B4     B5     B6     B7
801
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
802
    // B8     B9     BA     BB     BC     BD     BE     BF
803
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
804
    // C0     C1     C2     C3     C4     C5     C6     C7
805
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
806
    // C8     C9     CA     CB     CC     CD     CE     CF
807
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
808
    // D0     D1     D2     D3     D4     D5     D6     D7
809
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
810
    // D8     D9     DA     DB     DC     DD     DE     DF
811
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
812
    // E0     E1     E2     E3     E4     E5     E6     E7
813
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
814
    // E8     E9     EA     EB     EC     ED     EE     EF
815
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
816
    // F0     F1     F2     F3     F4     F5     F6     F7
817
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
818
    // F8     F9     FA     FB     FC     FD     FE     FF
819
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
820
821
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32] = {
822
    // 00     01     02     03     04     05     06     07
823
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
824
    // 08     09     0A     0B     0C     0D     0E     0F
825
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
826
    // 10     11     12     13     14     15     16     17
827
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
828
    // 18     19     1A     1B     1C     1D     1E     1F
829
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
830
    // 20     21     22     23     24     25     26     27
831
    0x01 | 0x00 | 0x04 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
832
    // 28     29     2A     2B     2C     2D     2E     2F
833
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
834
    // 30     31     32     33     34     35     36     37
835
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
836
    // 38     39     3A     3B     3C     3D     3E     3F
837
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
838
    // 40     41     42     43     44     45     46     47
839
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
840
    // 48     49     4A     4B     4C     4D     4E     4F
841
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
842
    // 50     51     52     53     54     55     56     57
843
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
844
    // 58     59     5A     5B     5C     5D     5E     5F
845
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
846
    // 60     61     62     63     64     65     66     67
847
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
848
    // 68     69     6A     6B     6C     6D     6E     6F
849
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
850
    // 70     71     72     73     74     75     76     77
851
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
852
    // 78     79     7A     7B     7C     7D     7E     7F
853
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
854
    // 80     81     82     83     84     85     86     87
855
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
856
    // 88     89     8A     8B     8C     8D     8E     8F
857
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
858
    // 90     91     92     93     94     95     96     97
859
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
860
    // 98     99     9A     9B     9C     9D     9E     9F
861
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
862
    // A0     A1     A2     A3     A4     A5     A6     A7
863
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
864
    // A8     A9     AA     AB     AC     AD     AE     AF
865
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
866
    // B0     B1     B2     B3     B4     B5     B6     B7
867
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
868
    // B8     B9     BA     BB     BC     BD     BE     BF
869
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
870
    // C0     C1     C2     C3     C4     C5     C6     C7
871
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
872
    // C8     C9     CA     CB     CC     CD     CE     CF
873
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
874
    // D0     D1     D2     D3     D4     D5     D6     D7
875
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
876
    // D8     D9     DA     DB     DC     DD     DE     DF
877
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
878
    // E0     E1     E2     E3     E4     E5     E6     E7
879
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
880
    // E8     E9     EA     EB     EC     ED     EE     EF
881
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
882
    // F0     F1     F2     F3     F4     F5     F6     F7
883
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
884
    // F8     F9     FA     FB     FC     FD     FE     FF
885
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
886
887
constexpr uint8_t USERINFO_PERCENT_ENCODE[32] = {
888
    // 00     01     02     03     04     05     06     07
889
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
890
    // 08     09     0A     0B     0C     0D     0E     0F
891
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
892
    // 10     11     12     13     14     15     16     17
893
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
894
    // 18     19     1A     1B     1C     1D     1E     1F
895
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
896
    // 20     21     22     23     24     25     26     27
897
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
898
    // 28     29     2A     2B     2C     2D     2E     2F
899
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
900
    // 30     31     32     33     34     35     36     37
901
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
902
    // 38     39     3A     3B     3C     3D     3E     3F
903
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
904
    // 40     41     42     43     44     45     46     47
905
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
906
    // 48     49     4A     4B     4C     4D     4E     4F
907
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
908
    // 50     51     52     53     54     55     56     57
909
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
910
    // 58     59     5A     5B     5C     5D     5E     5F
911
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
912
    // 60     61     62     63     64     65     66     67
913
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
914
    // 68     69     6A     6B     6C     6D     6E     6F
915
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
916
    // 70     71     72     73     74     75     76     77
917
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
918
    // 78     79     7A     7B     7C     7D     7E     7F
919
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80,
920
    // 80     81     82     83     84     85     86     87
921
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
922
    // 88     89     8A     8B     8C     8D     8E     8F
923
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
924
    // 90     91     92     93     94     95     96     97
925
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
926
    // 98     99     9A     9B     9C     9D     9E     9F
927
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
928
    // A0     A1     A2     A3     A4     A5     A6     A7
929
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
930
    // A8     A9     AA     AB     AC     AD     AE     AF
931
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
932
    // B0     B1     B2     B3     B4     B5     B6     B7
933
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
934
    // B8     B9     BA     BB     BC     BD     BE     BF
935
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
936
    // C0     C1     C2     C3     C4     C5     C6     C7
937
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
938
    // C8     C9     CA     CB     CC     CD     CE     CF
939
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
940
    // D0     D1     D2     D3     D4     D5     D6     D7
941
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
942
    // D8     D9     DA     DB     DC     DD     DE     DF
943
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
944
    // E0     E1     E2     E3     E4     E5     E6     E7
945
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
946
    // E8     E9     EA     EB     EC     ED     EE     EF
947
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
948
    // F0     F1     F2     F3     F4     F5     F6     F7
949
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
950
    // F8     F9     FA     FB     FC     FD     FE     FF
951
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
952
953
constexpr uint8_t PATH_PERCENT_ENCODE[32] = {
954
    // 00     01     02     03     04     05     06     07
955
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
956
    // 08     09     0A     0B     0C     0D     0E     0F
957
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
958
    // 10     11     12     13     14     15     16     17
959
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
960
    // 18     19     1A     1B     1C     1D     1E     1F
961
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
962
    // 20     21     22     23     24     25     26     27
963
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
964
    // 28     29     2A     2B     2C     2D     2E     2F
965
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
966
    // 30     31     32     33     34     35     36     37
967
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
968
    // 38     39     3A     3B     3C     3D     3E     3F
969
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
970
    // 40     41     42     43     44     45     46     47
971
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
972
    // 48     49     4A     4B     4C     4D     4E     4F
973
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
974
    // 50     51     52     53     54     55     56     57
975
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
976
    // 58     59     5A     5B     5C     5D     5E     5F
977
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x40 | 0x00,
978
    // 60     61     62     63     64     65     66     67
979
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
980
    // 68     69     6A     6B     6C     6D     6E     6F
981
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
982
    // 70     71     72     73     74     75     76     77
983
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
984
    // 78     79     7A     7B     7C     7D     7E     7F
985
    0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80,
986
    // 80     81     82     83     84     85     86     87
987
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
988
    // 88     89     8A     8B     8C     8D     8E     8F
989
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
990
    // 90     91     92     93     94     95     96     97
991
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
992
    // 98     99     9A     9B     9C     9D     9E     9F
993
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
994
    // A0     A1     A2     A3     A4     A5     A6     A7
995
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
996
    // A8     A9     AA     AB     AC     AD     AE     AF
997
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
998
    // B0     B1     B2     B3     B4     B5     B6     B7
999
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1000
    // B8     B9     BA     BB     BC     BD     BE     BF
1001
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1002
    // C0     C1     C2     C3     C4     C5     C6     C7
1003
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1004
    // C8     C9     CA     CB     CC     CD     CE     CF
1005
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1006
    // D0     D1     D2     D3     D4     D5     D6     D7
1007
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1008
    // D8     D9     DA     DB     DC     DD     DE     DF
1009
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1010
    // E0     E1     E2     E3     E4     E5     E6     E7
1011
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1012
    // E8     E9     EA     EB     EC     ED     EE     EF
1013
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1014
    // F0     F1     F2     F3     F4     F5     F6     F7
1015
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1016
    // F8     F9     FA     FB     FC     FD     FE     FF
1017
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1018
1019
constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = {
1020
    // 00     01     02     03     04     05     06     07
1021
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1022
    // 08     09     0A     0B     0C     0D     0E     0F
1023
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1024
    // 10     11     12     13     14     15     16     17
1025
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1026
    // 18     19     1A     1B     1C     1D     1E     1F
1027
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1028
    // 20     21     22     23     24     25     26     27
1029
    0x00 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1030
    // 28     29     2A     2B     2C     2D     2E     2F
1031
    0x01 | 0x02 | 0x00 | 0x08 | 0x10 | 0x00 | 0x00 | 0x80,
1032
    // 30     31     32     33     34     35     36     37
1033
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1034
    // 38     39     3A     3B     3C     3D     3E     3F
1035
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1036
    // 40     41     42     43     44     45     46     47
1037
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1038
    // 48     49     4A     4B     4C     4D     4E     4F
1039
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1040
    // 50     51     52     53     54     55     56     57
1041
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1042
    // 58     59     5A     5B     5C     5D     5E     5F
1043
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
1044
    // 60     61     62     63     64     65     66     67
1045
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1046
    // 68     69     6A     6B     6C     6D     6E     6F
1047
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1048
    // 70     71     72     73     74     75     76     77
1049
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1050
    // 78     79     7A     7B     7C     7D     7E     7F
1051
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1052
    // 80     81     82     83     84     85     86     87
1053
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1054
    // 88     89     8A     8B     8C     8D     8E     8F
1055
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1056
    // 90     91     92     93     94     95     96     97
1057
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1058
    // 98     99     9A     9B     9C     9D     9E     9F
1059
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1060
    // A0     A1     A2     A3     A4     A5     A6     A7
1061
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1062
    // A8     A9     AA     AB     AC     AD     AE     AF
1063
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1064
    // B0     B1     B2     B3     B4     B5     B6     B7
1065
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1066
    // B8     B9     BA     BB     BC     BD     BE     BF
1067
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1068
    // C0     C1     C2     C3     C4     C5     C6     C7
1069
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1070
    // C8     C9     CA     CB     CC     CD     CE     CF
1071
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1072
    // D0     D1     D2     D3     D4     D5     D6     D7
1073
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1074
    // D8     D9     DA     DB     DC     DD     DE     DF
1075
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1076
    // E0     E1     E2     E3     E4     E5     E6     E7
1077
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1078
    // E8     E9     EA     EB     EC     ED     EE     EF
1079
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1080
    // F0     F1     F2     F3     F4     F5     F6     F7
1081
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1082
    // F8     F9     FA     FB     FC     FD     FE     FF
1083
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1084
1085
241k
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1086
241k
  return !!(a[i >> 3] & (1 << (i & 7)));
1087
241k
}
1088
1089
}  // namespace ada::character_sets
1090
1091
#endif  // ADA_CHARACTER_SETS_INL_H
1092
/* end file include/ada/character_sets-inl.h */
1093
/* begin file include/ada/checkers-inl.h */
1094
/**
1095
 * @file checkers-inl.h
1096
 * @brief Definitions for URL specific checkers used within Ada.
1097
 */
1098
#ifndef ADA_CHECKERS_INL_H
1099
#define ADA_CHECKERS_INL_H
1100
1101
#include <bit>
1102
#include <cstddef>
1103
#include <cstdint>
1104
#include <string_view>
1105
/* begin file include/ada/checkers.h */
1106
/**
1107
 * @file checkers.h
1108
 * @brief Declarations for URL specific checkers used within Ada.
1109
 */
1110
#ifndef ADA_CHECKERS_H
1111
#define ADA_CHECKERS_H
1112
1113
1114
#include <cstdint>
1115
#include <cstring>
1116
#include <string_view>
1117
1118
/**
1119
 * These functions are not part of our public API and may
1120
 * change at any time.
1121
 * @private
1122
 * @namespace ada::checkers
1123
 * @brief Includes the definitions for validation functions
1124
 */
1125
namespace ada::checkers {
1126
1127
/**
1128
 * @private
1129
 * Assuming that x is an ASCII letter, this function returns the lower case
1130
 * equivalent.
1131
 * @details More likely to be inlined by the compiler and constexpr.
1132
 */
1133
constexpr char to_lower(char x) noexcept;
1134
1135
/**
1136
 * @private
1137
 * Returns true if the character is an ASCII letter. Equivalent to std::isalpha
1138
 * but more likely to be inlined by the compiler.
1139
 *
1140
 * @attention std::isalpha is not constexpr generally.
1141
 */
1142
constexpr bool is_alpha(char x) noexcept;
1143
1144
/**
1145
 * @private
1146
 * Check whether x is an ASCII digit. More likely to be inlined than
1147
 * std::isdigit.
1148
 */
1149
constexpr bool is_digit(char x) noexcept;
1150
1151
/**
1152
 * @private
1153
 * @details A string starts with a Windows drive letter if all of the following
1154
 * are true:
1155
 *
1156
 *   - its length is greater than or equal to 2
1157
 *   - its first two code points are a Windows drive letter
1158
 *   - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F
1159
 * (?), or U+0023 (#).
1160
 *
1161
 * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
1162
 */
1163
inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept;
1164
1165
/**
1166
 * @private
1167
 * @details A normalized Windows drive letter is a Windows drive letter of which
1168
 * the second code point is U+003A (:).
1169
 */
1170
inline constexpr bool is_normalized_windows_drive_letter(
1171
    std::string_view input) noexcept;
1172
1173
/**
1174
 * @private
1175
 * Returns true if an input is an ipv4 address. It is assumed that the string
1176
 * does not contain uppercase ASCII characters (the input should have been
1177
 * lowered cased before calling this function) and is not empty.
1178
 */
1179
ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept;
1180
1181
/**
1182
 * @private
1183
 * Cheap pre-check for the WHATWG "ends in a number" / IPv4-host cases.
1184
 * False means the host cannot be IPv4: the last label is empty, does not
1185
 * start with an ASCII digit, or contains a character other than a hex
1186
 * digit or x/X. True means the slower IPv4 parsers should run.
1187
 *
1188
 * Safe on mixed-case input (unlike is_ipv4, which expects a lowercased
1189
 * host).
1190
 */
1191
ada_really_inline constexpr bool last_label_may_be_a_number(
1192
    std::string_view view) noexcept;
1193
1194
/**
1195
 * @private
1196
 * Returns a bitset. If the first bit is set, then at least one character needs
1197
 * percent encoding. If the second bit is set, a \\ is found. If the third bit
1198
 * is set then we have a dot. If the fourth bit is set, then we have a percent
1199
 * character.
1200
 */
1201
ada_really_inline constexpr uint8_t path_signature(
1202
    std::string_view input) noexcept;
1203
1204
/**
1205
 * @private
1206
 * Returns true if the length of the domain name and its labels are according to
1207
 * the specifications. The length of the domain must be 255 octets (253
1208
 * characters not including the last 2 which are the empty label reserved at the
1209
 * end). When the empty label is included (a dot at the end), the domain name
1210
 * can have 254 characters. The length of a label must be at least 1 and at most
1211
 * 63 characters.
1212
 * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034
1213
 * @see https://www.unicode.org/reports/tr46/#ToASCII
1214
 */
1215
ada_really_inline constexpr bool verify_dns_length(
1216
    std::string_view input) noexcept;
1217
1218
/**
1219
 * @private
1220
 * Fast-path parser for pure decimal IPv4 addresses (e.g., "192.168.1.1").
1221
 * Returns the packed 32-bit IPv4 address on success, or a value > 0xFFFFFFFF
1222
 * to indicate failure (caller should fall back to general parser).
1223
 * This is optimized for the common case where the input is a well-formed
1224
 * decimal IPv4 address with exactly 4 octets.
1225
 */
1226
ada_really_inline uint64_t try_parse_ipv4_fast(std::string_view input) noexcept;
1227
1228
/**
1229
 * Sentinel value indicating try_parse_ipv4_fast() did not succeed.
1230
 * Any value > 0xFFFFFFFF indicates the fast path should not be used.
1231
 */
1232
constexpr uint64_t ipv4_fast_fail = uint64_t(1) << 32;
1233
1234
}  // namespace ada::checkers
1235
1236
#endif  // ADA_CHECKERS_H
1237
/* end file include/ada/checkers.h */
1238
1239
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1240
#include <immintrin.h>
1241
#endif
1242
1243
namespace ada::checkers {
1244
1245
23.8k
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1246
1247
44.0k
constexpr bool is_ipv4_number_char(char x) noexcept {
1248
44.0k
  const unsigned char c = static_cast<unsigned char>(x);
1249
44.0k
  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
1250
12.5k
         (c >= 'A' && c <= 'F') || c == 'x' || c == 'X';
1251
44.0k
}
1252
1253
11.2k
constexpr bool last_label_may_be_a_number(std::string_view view) noexcept {
1254
11.2k
  if (view.empty()) {
1255
0
    return false;
1256
0
  }
1257
11.2k
  const char* start = view.data();
1258
11.2k
  const char* end = start + view.size();
1259
11.2k
  if (end[-1] == '.') {
1260
443
    --end;
1261
443
    if (end == start) {
1262
258
      return false;
1263
258
    }
1264
443
  }
1265
11.0k
  if (!is_ipv4_number_char(end[-1])) {
1266
1.38k
    return false;
1267
1.38k
  }
1268
9.64k
  const char* label = end;
1269
34.4k
  while (label != start && is_ipv4_number_char(label[-1])) {
1270
24.7k
    --label;
1271
24.7k
  }
1272
9.64k
  if (label != start && label[-1] != '.') {
1273
303
    return false;
1274
303
  }
1275
9.34k
  return label != end && is_digit(*label);
1276
9.64k
}
1277
1278
33.1k
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1279
1280
18.3k
constexpr bool is_alpha(char x) noexcept {
1281
18.3k
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1282
18.3k
}
1283
1284
7.11k
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1285
7.11k
  return input.size() >= 2 &&
1286
6.17k
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1287
455
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1288
246
                                  input[2] == '?' || input[2] == '#'));
1289
7.11k
}
1290
1291
constexpr bool is_normalized_windows_drive_letter(
1292
7.11k
    std::string_view input) noexcept {
1293
7.11k
  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
1294
7.11k
}
1295
1296
namespace detail {
1297
1298
// Unrolled pure-decimal IPv4. The common portable path for 7-16 byte hosts.
1299
ada_really_inline uint64_t
1300
2.76k
parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
1301
2.76k
  uint32_t ipv4 = 0;
1302
4.91k
  for (int i = 0; i < 4; ++i) {
1303
4.42k
    if (p == pend) [[unlikely]] {
1304
3
      return ipv4_fast_fail;
1305
3
    }
1306
4.42k
    uint32_t val;
1307
4.42k
    char c = *p;
1308
4.42k
    if (c >= '0' && c <= '9') [[likely]] {
1309
2.67k
      val = static_cast<uint32_t>(c - '0');
1310
2.67k
      ++p;
1311
2.67k
    } else {
1312
1.75k
      return ipv4_fast_fail;
1313
1.75k
    }
1314
2.67k
    if (p < pend) {
1315
2.46k
      c = *p;
1316
2.46k
      if (c >= '0' && c <= '9') {
1317
1.31k
        if (val == 0) [[unlikely]] {
1318
75
          return ipv4_fast_fail;
1319
75
        }
1320
1.24k
        val = val * 10u + static_cast<uint32_t>(c - '0');
1321
1.24k
        ++p;
1322
1.24k
        if (p < pend) {
1323
1.10k
          c = *p;
1324
1.10k
          if (c >= '0' && c <= '9') {
1325
767
            val = val * 10u + static_cast<uint32_t>(c - '0');
1326
767
            ++p;
1327
767
            if (val > 255u) [[unlikely]] {
1328
134
              return ipv4_fast_fail;
1329
134
            }
1330
767
          }
1331
1.10k
        }
1332
1.24k
      }
1333
2.46k
    }
1334
2.46k
    ipv4 = (ipv4 << 8) | val;
1335
2.46k
    if (i < 3) {
1336
1.98k
      if (p == pend || *p != '.') [[unlikely]] {
1337
314
        return ipv4_fast_fail;
1338
314
      }
1339
1.66k
      ++p;
1340
1.66k
    }
1341
2.46k
  }
1342
483
  if (p != pend) {
1343
45
    if (p == pend - 1 && *p == '.') {
1344
4
      return ipv4;
1345
4
    }
1346
41
    return ipv4_fast_fail;
1347
45
  }
1348
438
  return ipv4;
1349
483
}
1350
1351
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1352
// Table-free AVX-512VL IPv4 parse (simdip parse_ipv4_avx512vl_notab5).
1353
// Needs VBMI2 for vpcompressb; ADA_AVX512 stays BW+VL (IPv6 does not use
1354
// VBMI2). Masked load of exactly `len` bytes (no over-read). Digit
1355
// placement is computed from compressed delimiter positions; octet > 255
1356
// is a dword compare on the zero-padded reversed digit group, in parallel
1357
// with the convert. Unusual-but-valid forms (octal, hex, leading zeros,
1358
// fewer than four parts) return ipv4_fast_fail so the general parser runs.
1359
ada_really_inline uint64_t try_parse_ipv4_avx512(const char* data,
1360
                                                 size_t len) noexcept {
1361
  // One trailing dot is WHATWG-legal ("1.2.3.4."); the SIMD kernel is
1362
  // strict four-group dotted-decimal.
1363
  if (data[len - 1] == '.') {
1364
    --len;
1365
  }
1366
  if (len > 15) [[unlikely]] {
1367
    return ipv4_fast_fail;
1368
  }
1369
1370
#if defined(__BMI2__)
1371
  const uint32_t len_mask = _bzhi_u32(0xFFFFFFFFu, static_cast<unsigned>(len));
1372
#else
1373
  const uint32_t len_mask = (1u << static_cast<unsigned>(len)) - 1u;
1374
#endif
1375
  const __mmask16 len_k = static_cast<__mmask16>(len_mask);
1376
  const __m128i dot = _mm_set1_epi8('.');
1377
  const __m128i v =
1378
      _mm_mask_loadu_epi8(dot, len_k, reinterpret_cast<const void*>(data));
1379
1380
  const __mmask16 delim = _mm_cmpeq_epi8_mask(v, dot);
1381
  const uint32_t dots = static_cast<uint32_t>(delim) & len_mask;
1382
  const uint32_t keep = len_mask & ~dots;
1383
1384
  const __m128i zero = _mm_set1_epi8('0');
1385
  const __m128i digits = _mm_sub_epi8(v, zero);
1386
  const __mmask16 is_digit = _mm_cmple_epu8_mask(digits, _mm_set1_epi8(9));
1387
  // Junk in a digit slot: inside [0, len) yet neither a digit nor a dot.
1388
  const __mmask16 hole = _kandn_mask16(_kor_mask16(delim, is_digit), len_k);
1389
  const __m128i v0 = _mm_maskz_mov_epi8(is_digit, digits);
1390
1391
  const __m128i iota =
1392
      _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
1393
  const __m128i c = _mm_maskz_compress_epi8(delim, iota);
1394
  const __m128i k_rep =
1395
      _mm_setr_epi8(0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3);
1396
  const __m128i qi = _mm_shuffle_epi8(c, k_rep);
1397
  const __m128i prev =
1398
      _mm_shuffle_epi8(_mm_alignr_epi8(c, _mm_set1_epi8(-1), 15), k_rep);
1399
  // Reversed group order: lane 4i+j fetches q_i-(j+1), so the dword is
1400
  // ones | tens<<8 | hundreds<<16, monotone in the decimal value.
1401
  const __m128i offr = _mm_setr_epi8(-1, -2, -3, -4, -1, -2, -3, -4, -1, -2, -3,
1402
                                     -4, -1, -2, -3, -4);
1403
  const __m128i idx = _mm_max_epi8(_mm_add_epi8(qi, offr), prev);
1404
  const __m128i padded = _mm_shuffle_epi8(v0, idx);
1405
1406
  const __m128i lim =
1407
      _mm_setr_epi8(5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0);
1408
  const __mmask8 over = _mm_cmpgt_epu32_mask(padded, lim);
1409
  const __m128i wtsr =
1410
      _mm_setr_epi8(1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0);
1411
#if defined(__AVX512VNNI__)
1412
  const __m128i res = _mm_dpbusd_epi32(_mm_setzero_si128(), padded, wtsr);
1413
#else
1414
  const __m128i res =
1415
      _mm_madd_epi16(_mm_maddubs_epi16(padded, wtsr), _mm_set1_epi16(1));
1416
#endif
1417
1418
  const __m128i gmin =
1419
      _mm_setr_epi8(1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1420
  const __m128i gmax =
1421
      _mm_setr_epi8(2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
1422
  const __m128i gap = _mm_sub_epi8(c, _mm_slli_si128(c, 1));
1423
  const __mmask16 bad_gap = _mm_cmpgt_epu8_mask(_mm_sub_epi8(gap, gmin), gmax);
1424
1425
  const uint32_t zero_bits =
1426
      static_cast<uint32_t>(_mm_cmpeq_epi8_mask(v, zero));
1427
  const uint32_t start_bits = (dots << 1) | 1u;
1428
1429
  const __mmask16 kerr =
1430
      _kor_mask16(_kor_mask16(hole, static_cast<__mmask16>(over)), bad_gap);
1431
  const uint32_t gerr = static_cast<uint32_t>(_mm_popcnt_u32(dots) ^ 3u) |
1432
                        (zero_bits & start_bits & (keep >> 1));
1433
1434
  if ((gerr | static_cast<uint32_t>(kerr)) == 0) [[likely]] {
1435
    const uint32_t packed =
1436
        static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_cvtepi32_epi8(res)));
1437
#if defined(__GNUC__) || defined(__clang__)
1438
    return static_cast<uint64_t>(__builtin_bswap32(packed));
1439
#else
1440
    return static_cast<uint64_t>(_byteswap_ulong(packed));
1441
#endif
1442
  }
1443
  return ipv4_fast_fail;
1444
}
1445
#endif  // ADA_AVX512 && __AVX512VBMI2__
1446
1447
}  // namespace detail
1448
1449
/**
1450
 * Fast pure-decimal IPv4 parse. Returns packed address or ipv4_fast_fail.
1451
 * Accepts an optional single trailing dot.
1452
 *
1453
 * On AVX-512BW+VL+VBMI2 targets, uses a table-free SIMD parse (masked
1454
 * load, no source over-read) based on parse_ipv4_avx512vl_notab5.
1455
 * Otherwise uses an unrolled scalar path (typically faster than SSE2/NEON
1456
 * pre-validation for these 7-16 byte hosts).
1457
 */
1458
ada_really_inline uint64_t
1459
14.4k
try_parse_ipv4_fast(std::string_view input) noexcept {
1460
14.4k
  const size_t len = input.size();
1461
  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
1462
14.4k
  if (len < 7 || len > 16) [[unlikely]] {
1463
11.6k
    return ipv4_fast_fail;
1464
11.6k
  }
1465
2.76k
  const char* data = input.data();
1466
1467
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1468
  return detail::try_parse_ipv4_avx512(data, len);
1469
#else
1470
2.76k
  return detail::parse_ipv4_decimal_scalar(data, data + len);
1471
14.4k
#endif
1472
14.4k
}
1473
1474
}  // namespace ada::checkers
1475
1476
#endif  // ADA_CHECKERS_INL_H
1477
/* end file include/ada/checkers-inl.h */
1478
/* begin file include/ada/log.h */
1479
/**
1480
 * @file log.h
1481
 * @brief Includes the definitions for logging.
1482
 * @private Excluded from docs through the doxygen file.
1483
 */
1484
#ifndef ADA_LOG_H
1485
#define ADA_LOG_H
1486
1487
// To enable logging, set ADA_LOGGING to 1:
1488
#ifndef ADA_LOGGING
1489
#define ADA_LOGGING 0
1490
#endif
1491
1492
#if ADA_LOGGING
1493
#include <iostream>
1494
#endif  // ADA_LOGGING
1495
1496
namespace ada {
1497
1498
/**
1499
 * Log a message. If you want to have no overhead when logging is disabled, use
1500
 * the ada_log macro.
1501
 * @private
1502
 */
1503
template <typename... Args>
1504
constexpr ada_really_inline void log([[maybe_unused]] Args... args) {
1505
#if ADA_LOGGING
1506
  ((std::cout << "ADA_LOG: ") << ... << args) << std::endl;
1507
#endif  // ADA_LOGGING
1508
}
1509
}  // namespace ada
1510
1511
#if ADA_LOGGING
1512
#ifndef ada_log
1513
#define ada_log(...)       \
1514
  do {                     \
1515
    ada::log(__VA_ARGS__); \
1516
  } while (0)
1517
#endif  // ada_log
1518
#else
1519
#define ada_log(...)
1520
#endif  // ADA_LOGGING
1521
1522
#endif  // ADA_LOG_H
1523
/* end file include/ada/log.h */
1524
/* begin file include/ada/encoding_type.h */
1525
/**
1526
 * @file encoding_type.h
1527
 * @brief Character encoding type definitions.
1528
 *
1529
 * Defines the encoding types supported for URL processing.
1530
 *
1531
 * @see https://encoding.spec.whatwg.org/
1532
 */
1533
#ifndef ADA_ENCODING_TYPE_H
1534
#define ADA_ENCODING_TYPE_H
1535
1536
#include <string>
1537
#include <string_view>
1538
1539
namespace ada {
1540
1541
/**
1542
 * @brief Character encoding types for URL processing.
1543
 *
1544
 * Specifies the character encoding used for percent-decoding and other
1545
 * string operations. UTF-8 is the most commonly used encoding for URLs.
1546
 *
1547
 * @see https://encoding.spec.whatwg.org/#encodings
1548
 */
1549
enum class encoding_type {
1550
  UTF8,     /**< UTF-8 encoding (default for URLs) */
1551
  UTF_16LE, /**< UTF-16 Little Endian encoding */
1552
  UTF_16BE, /**< UTF-16 Big Endian encoding */
1553
};
1554
1555
/**
1556
 * Converts an encoding_type to its string representation.
1557
 * @param type The encoding type to convert.
1558
 * @return A string view of the encoding name.
1559
 */
1560
ada_warn_unused std::string_view to_string(encoding_type type);
1561
1562
}  // namespace ada
1563
1564
#endif  // ADA_ENCODING_TYPE_H
1565
/* end file include/ada/encoding_type.h */
1566
/* begin file include/ada/helpers.h */
1567
/**
1568
 * @file helpers.h
1569
 * @brief Definitions for helper functions used within Ada.
1570
 */
1571
#ifndef ADA_HELPERS_H
1572
#define ADA_HELPERS_H
1573
1574
/* begin file include/ada/url_base.h */
1575
/**
1576
 * @file url_base.h
1577
 * @brief Base class and common definitions for URL types.
1578
 *
1579
 * This file defines the `url_base` abstract base class from which both
1580
 * `ada::url` and `ada::url_aggregator` inherit. It also defines common
1581
 * enumerations like `url_host_type`.
1582
 */
1583
#ifndef ADA_URL_BASE_H
1584
#define ADA_URL_BASE_H
1585
1586
/* begin file include/ada/scheme.h */
1587
/**
1588
 * @file scheme.h
1589
 * @brief URL scheme type definitions and utilities.
1590
 *
1591
 * This header defines the URL scheme types (http, https, etc.) and provides
1592
 * functions to identify special schemes and their default ports according
1593
 * to the WHATWG URL Standard.
1594
 *
1595
 * @see https://url.spec.whatwg.org/#special-scheme
1596
 */
1597
#ifndef ADA_SCHEME_H
1598
#define ADA_SCHEME_H
1599
1600
1601
#include <cstdint>
1602
#include <string>
1603
#include <string_view>
1604
1605
/**
1606
 * @namespace ada::scheme
1607
 * @brief URL scheme utilities and constants.
1608
 *
1609
 * Provides functions for working with URL schemes, including identification
1610
 * of special schemes and retrieval of default port numbers.
1611
 */
1612
namespace ada::scheme {
1613
1614
/**
1615
 * @brief Enumeration of URL scheme types.
1616
 *
1617
 * Special schemes have specific parsing rules and default ports.
1618
 * Using an enum allows efficient scheme comparisons without string operations.
1619
 *
1620
 * Default ports:
1621
 * - HTTP: 80
1622
 * - HTTPS: 443
1623
 * - WS: 80
1624
 * - WSS: 443
1625
 * - FTP: 21
1626
 * - FILE: (none)
1627
 */
1628
enum type : uint8_t {
1629
  HTTP = 0,        /**< http:// scheme (port 80) */
1630
  NOT_SPECIAL = 1, /**< Non-special scheme (no default port) */
1631
  HTTPS = 2,       /**< https:// scheme (port 443) */
1632
  WS = 3,          /**< ws:// WebSocket scheme (port 80) */
1633
  FTP = 4,         /**< ftp:// scheme (port 21) */
1634
  WSS = 5,         /**< wss:// secure WebSocket scheme (port 443) */
1635
  FILE = 6         /**< file:// scheme (no default port) */
1636
};
1637
1638
/**
1639
 * Checks if a scheme string is a special scheme.
1640
 * @param scheme The scheme string to check (e.g., "http", "https").
1641
 * @return `true` if the scheme is special, `false` otherwise.
1642
 * @see https://url.spec.whatwg.org/#special-scheme
1643
 */
1644
ada_really_inline constexpr bool is_special(std::string_view scheme);
1645
1646
/**
1647
 * Returns the default port for a special scheme string.
1648
 * @param scheme The scheme string (e.g., "http", "https").
1649
 * @return The default port number, or 0 if not a special scheme.
1650
 * @see https://url.spec.whatwg.org/#special-scheme
1651
 */
1652
constexpr uint16_t get_special_port(std::string_view scheme) noexcept;
1653
1654
/**
1655
 * Returns the default port for a scheme type.
1656
 * @param type The scheme type enum value.
1657
 * @return The default port number, or 0 if not applicable.
1658
 * @see https://url.spec.whatwg.org/#special-scheme
1659
 */
1660
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept;
1661
1662
/**
1663
 * Converts a scheme string to its type enum.
1664
 * @param scheme The scheme string to convert.
1665
 * @return The corresponding scheme type, or NOT_SPECIAL if not recognized.
1666
 */
1667
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept;
1668
1669
}  // namespace ada::scheme
1670
1671
#endif  // ADA_SCHEME_H
1672
/* end file include/ada/scheme.h */
1673
1674
#include <cstddef>
1675
#include <cstdint>
1676
#include <string>
1677
#include <string_view>
1678
1679
namespace ada {
1680
1681
/**
1682
 * @brief Enum representing the type of host in a URL.
1683
 *
1684
 * Used to distinguish between regular domain names, IPv4 addresses,
1685
 * and IPv6 addresses for proper parsing and serialization.
1686
 */
1687
enum url_host_type : uint8_t {
1688
  /** Regular domain name (e.g., "www.example.com") */
1689
  DEFAULT = 0,
1690
  /** IPv4 address (e.g., "127.0.0.1") */
1691
  IPV4 = 1,
1692
  /** IPv6 address (e.g., "[::1]" or "[2001:db8::1]") */
1693
  IPV6 = 2,
1694
};
1695
1696
/**
1697
 * @brief Abstract base class for URL representations.
1698
 *
1699
 * The `url_base` class provides the common interface and state shared by
1700
 * both `ada::url` and `ada::url_aggregator`. It contains basic URL attributes
1701
 * like validity status and scheme type, but delegates component storage and
1702
 * access to derived classes.
1703
 *
1704
 * @note This is an abstract class and cannot be instantiated directly.
1705
 *       Use `ada::url` or `ada::url_aggregator` instead.
1706
 *
1707
 * @see url
1708
 * @see url_aggregator
1709
 */
1710
struct url_base {
1711
15.9k
  virtual ~url_base() = default;
1712
1713
  /**
1714
   * Indicates whether the URL was successfully parsed.
1715
   * Set to `false` if parsing failed (e.g., invalid URL syntax).
1716
   */
1717
  bool is_valid{true};
1718
1719
  /**
1720
   * Indicates whether the URL has an opaque path (non-hierarchical).
1721
   * Opaque paths occur in non-special URLs like `mailto:` or `javascript:`.
1722
   */
1723
  bool has_opaque_path{false};
1724
1725
  /**
1726
   * The type of the URL's host (domain, IPv4, or IPv6).
1727
   */
1728
  url_host_type host_type = url_host_type::DEFAULT;
1729
1730
  /**
1731
   * @private
1732
   * Internal representation of the URL's scheme type.
1733
   */
1734
  ada::scheme::type type{ada::scheme::type::NOT_SPECIAL};
1735
1736
  /**
1737
   * Checks if the URL has a special scheme (http, https, ws, wss, ftp, file).
1738
   * Special schemes have specific parsing rules and default ports.
1739
   * @return `true` if the scheme is special, `false` otherwise.
1740
   */
1741
  [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept;
1742
1743
  /**
1744
   * Returns the URL's origin (scheme + host + port for special URLs).
1745
   * @return A newly allocated string containing the serialized origin.
1746
   * @see https://url.spec.whatwg.org/#concept-url-origin
1747
   */
1748
  [[nodiscard]] virtual std::string get_origin() const = 0;
1749
1750
  /**
1751
   * Validates whether the hostname is a valid domain according to RFC 1034.
1752
   * Checks that the domain and its labels have valid lengths.
1753
   * @return `true` if the domain is valid, `false` otherwise.
1754
   */
1755
  [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0;
1756
1757
  /**
1758
   * @private
1759
   * Returns the default port for special schemes (e.g., 443 for https).
1760
   * Returns 0 for file:// URLs or non-special schemes.
1761
   */
1762
  [[nodiscard]] inline uint16_t get_special_port() const noexcept;
1763
1764
  /**
1765
   * @private
1766
   * Returns the default port for the URL's scheme, or 0 if none.
1767
   */
1768
  [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept;
1769
1770
  /**
1771
   * @private
1772
   * Parses a port number from the input string.
1773
   * @param view The string containing the port to parse.
1774
   * @param check_trailing_content Whether to validate no trailing characters.
1775
   * @return Number of bytes consumed on success, 0 on failure.
1776
   */
1777
  virtual size_t parse_port(std::string_view view,
1778
                            bool check_trailing_content) = 0;
1779
1780
  /** @private */
1781
0
  virtual ada_really_inline size_t parse_port(std::string_view view) {
1782
0
    return this->parse_port(view, false);
1783
0
  }
1784
1785
  /**
1786
   * Returns a JSON string representation of this URL for debugging.
1787
   * @return A JSON-formatted string with URL information.
1788
   */
1789
  [[nodiscard]] virtual std::string to_string() const = 0;
1790
1791
  /** @private */
1792
  virtual inline void clear_pathname() = 0;
1793
1794
  /** @private */
1795
  virtual inline void clear_search() = 0;
1796
1797
  /** @private */
1798
  [[nodiscard]] virtual inline bool has_hash() const noexcept = 0;
1799
1800
  /** @private */
1801
  [[nodiscard]] virtual inline bool has_search() const noexcept = 0;
1802
1803
};  // url_base
1804
1805
}  // namespace ada
1806
1807
#endif
1808
/* end file include/ada/url_base.h */
1809
1810
#include <cstddef>
1811
#include <cstdint>
1812
#include <optional>
1813
#include <string>
1814
#include <string_view>
1815
#include <utility>
1816
1817
#if ADA_DEVELOPMENT_CHECKS
1818
#include <cstdlib>
1819
#include <iostream>
1820
#endif  // ADA_DEVELOPMENT_CHECKS
1821
1822
/**
1823
 * These functions are not part of our public API and may
1824
 * change at any time.
1825
 *
1826
 * @private
1827
 * @namespace ada::helpers
1828
 * @brief Includes the definitions for helper functions
1829
 */
1830
namespace ada::helpers {
1831
1832
/**
1833
 * @private
1834
 */
1835
template <typename out_iter>
1836
void encode_json(std::string_view view, out_iter out);
1837
1838
/**
1839
 * @private
1840
 * This function is used to prune a fragment from a url, and returning the
1841
 * removed string if input has fragment.
1842
 *
1843
 * @details prune_hash seeks the first '#' and returns everything after it
1844
 * as a string_view, and modifies (in place) the input so that it points at
1845
 * everything before the '#'. If no '#' is found, the input is left unchanged
1846
 * and std::nullopt is returned.
1847
 *
1848
 * @attention The function is non-allocating and it does not throw.
1849
 * @returns Note that the returned string_view might be empty!
1850
 */
1851
ada_really_inline std::optional<std::string_view> prune_hash(
1852
    std::string_view& input) noexcept;
1853
1854
/**
1855
 * @private
1856
 * Defined by the URL specification, shorten a URLs paths.
1857
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1858
 * @returns Returns true if path is shortened.
1859
 */
1860
ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type);
1861
1862
/**
1863
 * @private
1864
 * Defined by the URL specification, shorten a URLs paths.
1865
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1866
 * @returns Returns true if path is shortened.
1867
 */
1868
ada_really_inline bool shorten_path(std::string_view& path,
1869
                                    ada::scheme::type type);
1870
1871
/**
1872
 * @private
1873
 *
1874
 * Parse the path from the provided input and append to the existing
1875
 * (possibly empty) path. The input cannot contain tabs and spaces: it
1876
 * is the user's responsibility to check.
1877
 *
1878
 * The input is expected to be UTF-8.
1879
 *
1880
 * @see https://url.spec.whatwg.org/
1881
 */
1882
ada_really_inline void parse_prepared_path(std::string_view input,
1883
                                           ada::scheme::type type,
1884
                                           std::string& path);
1885
1886
/**
1887
 * @private
1888
 * Remove and mutate all ASCII tab or newline characters from an input.
1889
 */
1890
ada_really_inline void remove_ascii_tab_or_newline(std::string& input);
1891
1892
/**
1893
 * @private
1894
 * Return the substring from input going from index pos to the end.
1895
 */
1896
ada_really_inline constexpr std::string_view substring(std::string_view input,
1897
                                                       size_t pos);
1898
1899
/**
1900
 * @private
1901
 * Returns true if the string_view points within the string.
1902
 */
1903
bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1904
1905
/**
1906
 * @private
1907
 * Return the substring from input going from index pos1 to the pos2 (non
1908
 * included). The length of the substring is pos2 - pos1.
1909
 */
1910
ada_really_inline constexpr std::string_view substring(std::string_view input,
1911
                                                       size_t pos1,
1912
4.19k
                                                       size_t pos2) {
1913
#if ADA_DEVELOPMENT_CHECKS
1914
  if (pos2 < pos1) {
1915
    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1916
              << std::endl;
1917
    abort();
1918
  }
1919
#endif
1920
4.19k
  return input.substr(pos1, pos2 - pos1);
1921
4.19k
}
1922
1923
/**
1924
 * @private
1925
 * Modify the string_view so that it has the new size pos, assuming that pos <=
1926
 * input.size(). This function cannot throw.
1927
 */
1928
ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1929
1930
/**
1931
 * @private
1932
 * Returns a host's delimiter location depending on the state of the instance,
1933
 * and whether a colon was found outside brackets. Used by the host parser.
1934
 */
1935
ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1936
    bool is_special, std::string_view& view) noexcept;
1937
1938
/**
1939
 * @private
1940
 * Removes leading and trailing C0 control and whitespace characters from
1941
 * string.
1942
 */
1943
void trim_c0_whitespace(std::string_view& input) noexcept;
1944
1945
/**
1946
 * @private
1947
 * @see
1948
 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1949
 */
1950
template <class url_type>
1951
ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url);
1952
1953
/**
1954
 * @private
1955
 * Finds the delimiter of a view in authority state.
1956
 */
1957
ada_really_inline size_t
1958
find_authority_delimiter_special(std::string_view view) noexcept;
1959
1960
/**
1961
 * @private
1962
 * Finds the delimiter of a view in authority state.
1963
 */
1964
ada_really_inline size_t
1965
find_authority_delimiter(std::string_view view) noexcept;
1966
1967
/**
1968
 * @private
1969
 */
1970
template <typename T, typename... Args>
1971
48
inline void inner_concat(std::string& buffer, T t) {
1972
48
  buffer.append(t);
1973
48
}
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
1971
48
inline void inner_concat(std::string& buffer, T t) {
1972
48
  buffer.append(t);
1973
48
}
Unexecuted instantiation: void ada::helpers::inner_concat<char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
1974
1975
/**
1976
 * @private
1977
 */
1978
template <typename T, typename... Args>
1979
48
inline void inner_concat(std::string& buffer, T t, Args... args) {
1980
48
  buffer.append(t);
1981
48
  return inner_concat(buffer, args...);
1982
48
}
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
1979
48
inline void inner_concat(std::string& buffer, T t, Args... args) {
1980
48
  buffer.append(t);
1981
48
  return inner_concat(buffer, args...);
1982
48
}
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Unexecuted instantiation: void ada::helpers::inner_concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
1983
1984
/**
1985
 * @private
1986
 * Concatenate the arguments and return a string.
1987
 * @returns a string
1988
 */
1989
template <typename... Args>
1990
48
std::string concat(Args... args) {
1991
48
  std::string answer;
1992
48
  inner_concat(answer, args...);
1993
48
  return answer;
1994
48
}
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
1990
48
std::string concat(Args... args) {
1991
48
  std::string answer;
1992
48
  inner_concat(answer, args...);
1993
48
  return answer;
1994
48
}
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
1995
1996
/**
1997
 * @private
1998
 * @return Number of leading zeroes.
1999
 */
2000
12.2k
inline int leading_zeroes(uint32_t input_num) noexcept {
2001
#if ADA_REGULAR_VISUAL_STUDIO
2002
  unsigned long leading_zero(0);
2003
  unsigned long in(input_num);
2004
  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
2005
#else
2006
12.2k
  return __builtin_clz(input_num);
2007
12.2k
#endif  // ADA_REGULAR_VISUAL_STUDIO
2008
12.2k
}
2009
2010
/**
2011
 * @private
2012
 * Counts the number of decimal digits necessary to represent x.
2013
 * faster than std::to_string(x).size().
2014
 * @return digit count
2015
 */
2016
175
inline int fast_digit_count(uint32_t x) noexcept {
2017
175
  auto int_log2 = [](uint32_t z) -> int {
2018
175
    return 31 - ada::helpers::leading_zeroes(z | 1);
2019
175
  };
2020
  // Compiles to very few instructions. Note that the
2021
  // table is static and thus effectively a constant.
2022
  // We leave it inside the function because it is meaningless
2023
  // outside of it (this comes at no performance cost).
2024
175
  const static uint64_t table[] = {
2025
175
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
2026
175
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
2027
175
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
2028
175
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
2029
175
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
2030
175
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
2031
175
      42949672960, 42949672960};
2032
175
  return int((x + table[int_log2(x)]) >> 32);
2033
175
}
2034
}  // namespace ada::helpers
2035
2036
#endif  // ADA_HELPERS_H
2037
/* end file include/ada/helpers.h */
2038
/* begin file include/ada/parser.h */
2039
/**
2040
 * @file parser.h
2041
 * @brief Low-level URL parsing functions.
2042
 *
2043
 * This header provides the internal URL parsing implementation. Most users
2044
 * should use `ada::parse()` from implementation.h instead of these functions
2045
 * directly.
2046
 *
2047
 * @see implementation.h for the recommended public API
2048
 */
2049
#ifndef ADA_PARSER_H
2050
#define ADA_PARSER_H
2051
2052
#include <cstddef>
2053
#include <cstdint>
2054
#include <string_view>
2055
#include <variant>
2056
2057
/* begin file include/ada/expected.h */
2058
/**
2059
 * @file expected.h
2060
 * @brief Definitions for std::expected
2061
 * @private Excluded from docs through the doxygen file.
2062
 */
2063
///
2064
// expected - An implementation of std::expected with extensions
2065
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
2066
//
2067
// Documentation available at http://tl.tartanllama.xyz/
2068
//
2069
// To the extent possible under law, the author(s) have dedicated all
2070
// copyright and related and neighboring rights to this software to the
2071
// public domain worldwide. This software is distributed without any warranty.
2072
//
2073
// You should have received a copy of the CC0 Public Domain Dedication
2074
// along with this software. If not, see
2075
// <http://creativecommons.org/publicdomain/zero/1.0/>.
2076
///
2077
2078
#ifndef TL_EXPECTED_HPP
2079
#define TL_EXPECTED_HPP
2080
2081
#define TL_EXPECTED_VERSION_MAJOR 1
2082
#define TL_EXPECTED_VERSION_MINOR 1
2083
#define TL_EXPECTED_VERSION_PATCH 0
2084
2085
#include <cstddef>
2086
#include <exception>
2087
#include <functional>
2088
#include <initializer_list>
2089
#include <type_traits>
2090
#include <utility>
2091
2092
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
2093
#define TL_EXPECTED_EXCEPTIONS_ENABLED
2094
#endif
2095
2096
#if (defined(_MSC_VER) && _MSC_VER == 1900)
2097
#define TL_EXPECTED_MSVC2015
2098
#define TL_EXPECTED_MSVC2015_CONSTEXPR
2099
#else
2100
#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
2101
#endif
2102
2103
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2104
     !defined(__clang__))
2105
#define TL_EXPECTED_GCC49
2106
#endif
2107
2108
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
2109
     !defined(__clang__))
2110
#define TL_EXPECTED_GCC54
2111
#endif
2112
2113
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
2114
     !defined(__clang__))
2115
#define TL_EXPECTED_GCC55
2116
#endif
2117
2118
#if !defined(TL_ASSERT)
2119
// can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
2120
#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
2121
#include <cassert>
2122
2.80k
#define TL_ASSERT(x) assert(x)
2123
#else
2124
#define TL_ASSERT(x)
2125
#endif
2126
#endif
2127
2128
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2129
     !defined(__clang__))
2130
// GCC < 5 doesn't support overloading on const&& for member functions
2131
2132
#define TL_EXPECTED_NO_CONSTRR
2133
// GCC < 5 doesn't support some standard C++11 type traits
2134
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2135
  std::has_trivial_copy_constructor<T>
2136
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2137
  std::has_trivial_copy_assign<T>
2138
2139
// This one will be different for GCC 5.7 if it's ever supported
2140
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2141
  std::is_trivially_destructible<T>
2142
2143
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
2144
// std::vector for non-copyable types
2145
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
2146
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2147
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2148
namespace tl {
2149
namespace detail {
2150
template <class T>
2151
struct is_trivially_copy_constructible
2152
    : std::is_trivially_copy_constructible<T> {};
2153
#ifdef _GLIBCXX_VECTOR
2154
template <class T, class A>
2155
struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
2156
#endif
2157
}  // namespace detail
2158
}  // namespace tl
2159
#endif
2160
2161
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2162
  tl::detail::is_trivially_copy_constructible<T>
2163
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2164
  std::is_trivially_copy_assignable<T>
2165
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2166
  std::is_trivially_destructible<T>
2167
#else
2168
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2169
  std::is_trivially_copy_constructible<T>
2170
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2171
  std::is_trivially_copy_assignable<T>
2172
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2173
  std::is_trivially_destructible<T>
2174
#endif
2175
2176
#if __cplusplus > 201103L
2177
#define TL_EXPECTED_CXX14
2178
#endif
2179
2180
#ifdef TL_EXPECTED_GCC49
2181
#define TL_EXPECTED_GCC49_CONSTEXPR
2182
#else
2183
#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
2184
#endif
2185
2186
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
2187
     defined(TL_EXPECTED_GCC49))
2188
#define TL_EXPECTED_11_CONSTEXPR
2189
#else
2190
#define TL_EXPECTED_11_CONSTEXPR constexpr
2191
#endif
2192
2193
namespace tl {
2194
template <class T, class E>
2195
class expected;
2196
2197
#ifndef TL_MONOSTATE_INPLACE_MUTEX
2198
#define TL_MONOSTATE_INPLACE_MUTEX
2199
class monostate {};
2200
2201
struct in_place_t {
2202
  explicit in_place_t() = default;
2203
};
2204
static constexpr in_place_t in_place{};
2205
#endif
2206
2207
template <class E>
2208
class unexpected {
2209
 public:
2210
  static_assert(!std::is_same<E, void>::value, "E must not be void");
2211
2212
  unexpected() = delete;
2213
  constexpr explicit unexpected(const E& e) : m_val(e) {}
2214
2215
21.7k
  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
2216
2217
  template <class... Args, typename std::enable_if<std::is_constructible<
2218
                               E, Args&&...>::value>::type* = nullptr>
2219
  constexpr explicit unexpected(Args&&... args)
2220
0
      : m_val(std::forward<Args>(args)...) {}
2221
  template <
2222
      class U, class... Args,
2223
      typename std::enable_if<std::is_constructible<
2224
          E, std::initializer_list<U>&, Args&&...>::value>::type* = nullptr>
2225
  constexpr explicit unexpected(std::initializer_list<U> l, Args&&... args)
2226
      : m_val(l, std::forward<Args>(args)...) {}
2227
2228
  constexpr const E& value() const& { return m_val; }
2229
10.8k
  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
2230
  TL_EXPECTED_11_CONSTEXPR E&& value() && { return std::move(m_val); }
2231
  constexpr const E&& value() const&& { return std::move(m_val); }
2232
2233
 private:
2234
  E m_val;
2235
};
2236
2237
#ifdef __cpp_deduction_guides
2238
template <class E>
2239
unexpected(E) -> unexpected<E>;
2240
#endif
2241
2242
template <class E>
2243
constexpr bool operator==(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2244
  return lhs.value() == rhs.value();
2245
}
2246
template <class E>
2247
constexpr bool operator!=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2248
  return lhs.value() != rhs.value();
2249
}
2250
template <class E>
2251
constexpr bool operator<(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2252
  return lhs.value() < rhs.value();
2253
}
2254
template <class E>
2255
constexpr bool operator<=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2256
  return lhs.value() <= rhs.value();
2257
}
2258
template <class E>
2259
constexpr bool operator>(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2260
  return lhs.value() > rhs.value();
2261
}
2262
template <class E>
2263
constexpr bool operator>=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2264
  return lhs.value() >= rhs.value();
2265
}
2266
2267
template <class E>
2268
unexpected<typename std::decay<E>::type> make_unexpected(E&& e) {
2269
  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
2270
}
2271
2272
struct unexpect_t {
2273
  unexpect_t() = default;
2274
};
2275
static constexpr unexpect_t unexpect{};
2276
2277
namespace detail {
2278
template <typename E>
2279
0
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E&& e) {
2280
0
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2281
0
  throw std::forward<E>(e);
2282
#else
2283
  (void)e;
2284
#ifdef _MSC_VER
2285
  __assume(0);
2286
#else
2287
  __builtin_unreachable();
2288
#endif
2289
#endif
2290
0
}
2291
2292
#ifndef TL_TRAITS_MUTEX
2293
#define TL_TRAITS_MUTEX
2294
// C++14-style aliases for brevity
2295
template <class T>
2296
using remove_const_t = typename std::remove_const<T>::type;
2297
template <class T>
2298
using remove_reference_t = typename std::remove_reference<T>::type;
2299
template <class T>
2300
using decay_t = typename std::decay<T>::type;
2301
template <bool E, class T = void>
2302
using enable_if_t = typename std::enable_if<E, T>::type;
2303
template <bool B, class T, class F>
2304
using conditional_t = typename std::conditional<B, T, F>::type;
2305
2306
// std::conjunction from C++17
2307
template <class...>
2308
struct conjunction : std::true_type {};
2309
template <class B>
2310
struct conjunction<B> : B {};
2311
template <class B, class... Bs>
2312
struct conjunction<B, Bs...>
2313
    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2314
2315
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2316
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2317
#endif
2318
2319
// In C++11 mode, there's an issue in libc++'s std::mem_fn
2320
// which results in a hard-error when using it in a noexcept expression
2321
// in some cases. This is a check to workaround the common failing case.
2322
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2323
template <class T>
2324
struct is_pointer_to_non_const_member_func : std::false_type {};
2325
template <class T, class Ret, class... Args>
2326
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
2327
    : std::true_type {};
2328
template <class T, class Ret, class... Args>
2329
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
2330
    : std::true_type {};
2331
template <class T, class Ret, class... Args>
2332
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
2333
    : std::true_type {};
2334
template <class T, class Ret, class... Args>
2335
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
2336
    : std::true_type {};
2337
template <class T, class Ret, class... Args>
2338
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&>
2339
    : std::true_type {};
2340
template <class T, class Ret, class... Args>
2341
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&>
2342
    : std::true_type {};
2343
2344
template <class T>
2345
struct is_const_or_const_ref : std::false_type {};
2346
template <class T>
2347
struct is_const_or_const_ref<T const&> : std::true_type {};
2348
template <class T>
2349
struct is_const_or_const_ref<T const> : std::true_type {};
2350
#endif
2351
2352
// std::invoke from C++17
2353
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2354
template <
2355
    typename Fn, typename... Args,
2356
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2357
    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2358
                             is_const_or_const_ref<Args...>::value)>,
2359
#endif
2360
    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2361
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2362
    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2363
    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2364
  return std::mem_fn(f)(std::forward<Args>(args)...);
2365
}
2366
2367
template <typename Fn, typename... Args,
2368
          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2369
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2370
    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2371
    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2372
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
2373
}
2374
2375
// std::invoke_result from C++17
2376
template <class F, class, class... Us>
2377
struct invoke_result_impl;
2378
2379
template <class F, class... Us>
2380
struct invoke_result_impl<
2381
    F,
2382
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2383
    Us...> {
2384
  using type =
2385
      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2386
};
2387
2388
template <class F, class... Us>
2389
using invoke_result = invoke_result_impl<F, void, Us...>;
2390
2391
template <class F, class... Us>
2392
using invoke_result_t = typename invoke_result<F, Us...>::type;
2393
2394
#if defined(_MSC_VER) && _MSC_VER <= 1900
2395
// TODO make a version which works with MSVC 2015
2396
template <class T, class U = T>
2397
struct is_swappable : std::true_type {};
2398
2399
template <class T, class U = T>
2400
struct is_nothrow_swappable : std::true_type {};
2401
#else
2402
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2403
namespace swap_adl_tests {
2404
// if swap ADL finds this then it would call std::swap otherwise (same
2405
// signature)
2406
struct tag {};
2407
2408
template <class T>
2409
tag swap(T&, T&);
2410
template <class T, std::size_t N>
2411
tag swap(T (&a)[N], T (&b)[N]);
2412
2413
// helper functions to test if an unqualified swap is possible, and if it
2414
// becomes std::swap
2415
template <class, class>
2416
std::false_type can_swap(...) noexcept(false);
2417
template <class T, class U,
2418
          class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
2419
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(),
2420
                                                    std::declval<U&>())));
2421
2422
template <class, class>
2423
std::false_type uses_std(...);
2424
template <class T, class U>
2425
std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag>
2426
uses_std(int);
2427
2428
template <class T>
2429
struct is_std_swap_noexcept
2430
    : std::integral_constant<bool,
2431
                             std::is_nothrow_move_constructible<T>::value &&
2432
                                 std::is_nothrow_move_assignable<T>::value> {};
2433
2434
template <class T, std::size_t N>
2435
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2436
2437
template <class T, class U>
2438
struct is_adl_swap_noexcept
2439
    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2440
}  // namespace swap_adl_tests
2441
2442
template <class T, class U = T>
2443
struct is_swappable
2444
    : std::integral_constant<
2445
          bool,
2446
          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2447
              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2448
               (std::is_move_assignable<T>::value &&
2449
                std::is_move_constructible<T>::value))> {};
2450
2451
template <class T, std::size_t N>
2452
struct is_swappable<T[N], T[N]>
2453
    : std::integral_constant<
2454
          bool,
2455
          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2456
              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2457
                   0))::value ||
2458
               is_swappable<T, T>::value)> {};
2459
2460
template <class T, class U = T>
2461
struct is_nothrow_swappable
2462
    : std::integral_constant<
2463
          bool,
2464
          is_swappable<T, U>::value &&
2465
              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2466
                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2467
               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2468
                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2469
#endif
2470
#endif
2471
2472
// Trait for checking if a type is a tl::expected
2473
template <class T>
2474
struct is_expected_impl : std::false_type {};
2475
template <class T, class E>
2476
struct is_expected_impl<expected<T, E>> : std::true_type {};
2477
template <class T>
2478
using is_expected = is_expected_impl<decay_t<T>>;
2479
2480
template <class T, class E, class U>
2481
using expected_enable_forward_value = detail::enable_if_t<
2482
    std::is_constructible<T, U&&>::value &&
2483
    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2484
    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2485
    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2486
2487
template <class T, class E, class U, class G, class UR, class GR>
2488
using expected_enable_from_other = detail::enable_if_t<
2489
    std::is_constructible<T, UR>::value &&
2490
    std::is_constructible<E, GR>::value &&
2491
    !std::is_constructible<T, expected<U, G>&>::value &&
2492
    !std::is_constructible<T, expected<U, G>&&>::value &&
2493
    !std::is_constructible<T, const expected<U, G>&>::value &&
2494
    !std::is_constructible<T, const expected<U, G>&&>::value &&
2495
    !std::is_convertible<expected<U, G>&, T>::value &&
2496
    !std::is_convertible<expected<U, G>&&, T>::value &&
2497
    !std::is_convertible<const expected<U, G>&, T>::value &&
2498
    !std::is_convertible<const expected<U, G>&&, T>::value>;
2499
2500
template <class T, class U>
2501
using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2502
2503
template <class T>
2504
using is_copy_constructible_or_void =
2505
    is_void_or<T, std::is_copy_constructible<T>>;
2506
2507
template <class T>
2508
using is_move_constructible_or_void =
2509
    is_void_or<T, std::is_move_constructible<T>>;
2510
2511
template <class T>
2512
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2513
2514
template <class T>
2515
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2516
2517
}  // namespace detail
2518
2519
namespace detail {
2520
struct no_init_t {};
2521
static constexpr no_init_t no_init{};
2522
2523
// Implements the storage of the values, and ensures that the destructor is
2524
// trivial if it can be.
2525
//
2526
// This specialization is for where neither `T` or `E` is trivially
2527
// destructible, so the destructors must be called on destruction of the
2528
// `expected`
2529
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2530
          bool = std::is_trivially_destructible<E>::value>
2531
struct expected_storage_base {
2532
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2533
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2534
2535
  template <class... Args,
2536
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2537
                nullptr>
2538
  constexpr expected_storage_base(in_place_t, Args&&... args)
2539
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2540
2541
  template <class U, class... Args,
2542
            detail::enable_if_t<std::is_constructible<
2543
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2544
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2545
                                  Args&&... args)
2546
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2547
  template <class... Args,
2548
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2549
                nullptr>
2550
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2551
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2552
2553
  template <class U, class... Args,
2554
            detail::enable_if_t<std::is_constructible<
2555
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2556
  constexpr explicit expected_storage_base(unexpect_t,
2557
                                           std::initializer_list<U> il,
2558
                                           Args&&... args)
2559
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2560
2561
  ~expected_storage_base() {
2562
    if (m_has_val) {
2563
      m_val.~T();
2564
    } else {
2565
      m_unexpect.~unexpected<E>();
2566
    }
2567
  }
2568
  union {
2569
    T m_val;
2570
    unexpected<E> m_unexpect;
2571
    char m_no_init;
2572
  };
2573
  bool m_has_val;
2574
};
2575
2576
// This specialization is for when both `T` and `E` are trivially-destructible,
2577
// so the destructor of the `expected` can be trivial.
2578
template <class T, class E>
2579
struct expected_storage_base<T, E, true, true> {
2580
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2581
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2582
2583
  template <class... Args,
2584
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2585
                nullptr>
2586
  constexpr expected_storage_base(in_place_t, Args&&... args)
2587
0
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS4_11char_traitsIcEEEELNS2_27url_search_params_iter_typeE0EEENS2_6errorsELb1ELb1EEC2IJSA_ETnPNS4_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS4_11char_traitsIcEEEELNS2_27url_search_params_iter_typeE1EEENS2_6errorsELb1ELb1EEC2IJSA_ETnPNS4_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada22url_search_params_iterINSt3__14pairINS4_17basic_string_viewIcNS4_11char_traitsIcEEEES9_EELNS2_27url_search_params_iter_typeE2EEENS2_6errorsELb1ELb1EEC2IJSC_ETnPNS4_9enable_ifIXsr3std16is_constructibleISC_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
2588
2589
  template <class U, class... Args,
2590
            detail::enable_if_t<std::is_constructible<
2591
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2592
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2593
                                  Args&&... args)
2594
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2595
  template <class... Args,
2596
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2597
                nullptr>
2598
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2599
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2600
2601
  template <class U, class... Args,
2602
            detail::enable_if_t<std::is_constructible<
2603
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2604
  constexpr explicit expected_storage_base(unexpect_t,
2605
                                           std::initializer_list<U> il,
2606
                                           Args&&... args)
2607
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2608
2609
  ~expected_storage_base() = default;
2610
  union {
2611
    T m_val;
2612
    unexpected<E> m_unexpect;
2613
    char m_no_init;
2614
  };
2615
  bool m_has_val;
2616
};
2617
2618
// T is trivial, E is not.
2619
template <class T, class E>
2620
struct expected_storage_base<T, E, true, false> {
2621
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2622
  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2623
      : m_no_init(), m_has_val(false) {}
2624
2625
  template <class... Args,
2626
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2627
                nullptr>
2628
  constexpr expected_storage_base(in_place_t, Args&&... args)
2629
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2630
2631
  template <class U, class... Args,
2632
            detail::enable_if_t<std::is_constructible<
2633
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2634
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2635
                                  Args&&... args)
2636
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2637
  template <class... Args,
2638
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2639
                nullptr>
2640
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2641
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2642
2643
  template <class U, class... Args,
2644
            detail::enable_if_t<std::is_constructible<
2645
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2646
  constexpr explicit expected_storage_base(unexpect_t,
2647
                                           std::initializer_list<U> il,
2648
                                           Args&&... args)
2649
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2650
2651
  ~expected_storage_base() {
2652
    if (!m_has_val) {
2653
      m_unexpect.~unexpected<E>();
2654
    }
2655
  }
2656
2657
  union {
2658
    T m_val;
2659
    unexpected<E> m_unexpect;
2660
    char m_no_init;
2661
  };
2662
  bool m_has_val;
2663
};
2664
2665
// E is trivial, T is not.
2666
template <class T, class E>
2667
struct expected_storage_base<T, E, false, true> {
2668
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2669
0
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2670
2671
  template <class... Args,
2672
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2673
                nullptr>
2674
  constexpr expected_storage_base(in_place_t, Args&&... args)
2675
2.52k
      : 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
2675
2.52k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJRA1_KcETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJRA2_KcETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJRS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada23url_pattern_list_detail15compiled_routesENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESC_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada17url_search_paramsENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEENS7_IS9_EEEEN3ada6errorsELb0ELb1EEC2IJSB_ETnPNS2_9enable_ifIXsr3std16is_constructibleISB_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada16url_pattern_partENS2_9allocatorIS5_EEEENS4_6errorsELb0ELb1EEC2IJRS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
2676
2677
  template <class U, class... Args,
2678
            detail::enable_if_t<std::is_constructible<
2679
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2680
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2681
                                  Args&&... args)
2682
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2683
  template <class... Args,
2684
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2685
                nullptr>
2686
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2687
10.8k
      : 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
2687
10.8k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada23url_pattern_list_detail15compiled_routesENS2_6errorsELb0ELb1EEC2IJS5_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESC_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada16url_pattern_partENS2_9allocatorIS5_EEEENS4_6errorsELb0ELb1EEC2IJS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESF_
2688
2689
  template <class U, class... Args,
2690
            detail::enable_if_t<std::is_constructible<
2691
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2692
  constexpr explicit expected_storage_base(unexpect_t,
2693
                                           std::initializer_list<U> il,
2694
                                           Args&&... args)
2695
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2696
2697
13.4k
  ~expected_storage_base() {
2698
13.4k
    if (m_has_val) {
2699
2.52k
      m_val.~T();
2700
2.52k
    }
2701
13.4k
  }
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
2697
13.4k
  ~expected_storage_base() {
2698
13.4k
    if (m_has_val) {
2699
2.52k
      m_val.~T();
2700
2.52k
    }
2701
13.4k
  }
Unexecuted instantiation: tl::detail::expected_storage_base<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors, false, true>::~expected_storage_base()
Unexecuted instantiation: tl::detail::expected_storage_base<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors, false, true>::~expected_storage_base()
Unexecuted instantiation: tl::detail::expected_storage_base<ada::url_search_params, ada::errors, false, true>::~expected_storage_base()
Unexecuted instantiation: tl::detail::expected_storage_base<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors, false, true>::~expected_storage_base()
Unexecuted instantiation: tl::detail::expected_storage_base<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors, false, true>::~expected_storage_base()
2702
  union {
2703
    T m_val;
2704
    unexpected<E> m_unexpect;
2705
    char m_no_init;
2706
  };
2707
  bool m_has_val;
2708
};
2709
2710
// `T` is `void`, `E` is trivially-destructible
2711
template <class E>
2712
struct expected_storage_base<void, E, false, true> {
2713
#if __GNUC__ <= 5
2714
// no constexpr for GCC 4/5 bug
2715
#else
2716
  TL_EXPECTED_MSVC2015_CONSTEXPR
2717
#endif
2718
  expected_storage_base() : m_has_val(true) {}
2719
2720
  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2721
2722
  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2723
2724
  template <class... Args,
2725
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2726
                nullptr>
2727
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2728
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2729
2730
  template <class U, class... Args,
2731
            detail::enable_if_t<std::is_constructible<
2732
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2733
  constexpr explicit expected_storage_base(unexpect_t,
2734
                                           std::initializer_list<U> il,
2735
                                           Args&&... args)
2736
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2737
2738
  ~expected_storage_base() = default;
2739
  struct dummy {};
2740
  union {
2741
    unexpected<E> m_unexpect;
2742
    dummy m_val;
2743
  };
2744
  bool m_has_val;
2745
};
2746
2747
// `T` is `void`, `E` is not trivially-destructible
2748
template <class E>
2749
struct expected_storage_base<void, E, false, false> {
2750
  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2751
  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2752
2753
  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2754
2755
  template <class... Args,
2756
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2757
                nullptr>
2758
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2759
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2760
2761
  template <class U, class... Args,
2762
            detail::enable_if_t<std::is_constructible<
2763
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2764
  constexpr explicit expected_storage_base(unexpect_t,
2765
                                           std::initializer_list<U> il,
2766
                                           Args&&... args)
2767
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2768
2769
  ~expected_storage_base() {
2770
    if (!m_has_val) {
2771
      m_unexpect.~unexpected<E>();
2772
    }
2773
  }
2774
2775
  union {
2776
    unexpected<E> m_unexpect;
2777
    char m_dummy;
2778
  };
2779
  bool m_has_val;
2780
};
2781
2782
// This base class provides some handy member functions which can be used in
2783
// further derived classes
2784
template <class T, class E>
2785
struct expected_operations_base : expected_storage_base<T, E> {
2786
  using expected_storage_base<T, E>::expected_storage_base;
2787
2788
  template <class... Args>
2789
  void construct(Args&&... args) noexcept {
2790
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2791
    this->m_has_val = true;
2792
  }
2793
2794
  template <class Rhs>
2795
  // NOLINTNEXTLINE(bugprone-exception-escape)
2796
0
  void construct_with(Rhs&& rhs) noexcept {
2797
0
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2798
0
    this->m_has_val = true;
2799
0
  }
2800
2801
  template <class... Args>
2802
0
  void construct_error(Args&&... args) noexcept {
2803
0
    new (std::addressof(this->m_unexpect))
2804
0
        unexpected<E>(std::forward<Args>(args)...);
2805
0
    this->m_has_val = false;
2806
0
  }
2807
2808
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2809
2810
  // These assign overloads ensure that the most efficient assignment
2811
  // implementation is used while maintaining the strong exception guarantee.
2812
  // The problematic case is where rhs has a value, but *this does not.
2813
  //
2814
  // This overload handles the case where we can just copy-construct `T`
2815
  // directly into place without throwing.
2816
  template <class U = T,
2817
            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>* =
2818
                nullptr>
2819
  void assign(const expected_operations_base& rhs) noexcept {
2820
    if (!this->m_has_val && rhs.m_has_val) {
2821
      geterr().~unexpected<E>();
2822
      construct(rhs.get());
2823
    } else {
2824
      assign_common(rhs);
2825
    }
2826
  }
2827
2828
  // This overload handles the case where we can attempt to create a copy of
2829
  // `T`, then no-throw move it into place if the copy was successful.
2830
  template <class U = T,
2831
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2832
                                std::is_nothrow_move_constructible<U>::value>* =
2833
                nullptr>
2834
  void assign(const expected_operations_base& rhs) noexcept {
2835
    if (!this->m_has_val && rhs.m_has_val) {
2836
      T tmp = rhs.get();
2837
      geterr().~unexpected<E>();
2838
      construct(std::move(tmp));
2839
    } else {
2840
      assign_common(rhs);
2841
    }
2842
  }
2843
2844
  // This overload is the worst-case, where we have to move-construct the
2845
  // unexpected value into temporary storage, then try to copy the T into place.
2846
  // If the construction succeeds, then everything is fine, but if it throws,
2847
  // then we move the old unexpected value back into place before rethrowing the
2848
  // exception.
2849
  template <class U = T,
2850
            detail::enable_if_t<
2851
                !std::is_nothrow_copy_constructible<U>::value &&
2852
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2853
  void assign(const expected_operations_base& rhs) {
2854
    if (!this->m_has_val && rhs.m_has_val) {
2855
      auto tmp = std::move(geterr());
2856
      geterr().~unexpected<E>();
2857
2858
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2859
      try {
2860
        construct(rhs.get());
2861
      } catch (...) {
2862
        geterr() = std::move(tmp);
2863
        throw;
2864
      }
2865
#else
2866
      construct(rhs.get());
2867
#endif
2868
    } else {
2869
      assign_common(rhs);
2870
    }
2871
  }
2872
2873
  // These overloads do the same as above, but for rvalues
2874
  template <class U = T,
2875
            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>* =
2876
                nullptr>
2877
  void assign(expected_operations_base&& rhs) noexcept {
2878
    if (!this->m_has_val && rhs.m_has_val) {
2879
      geterr().~unexpected<E>();
2880
      construct(std::move(rhs).get());
2881
    } else {
2882
      assign_common(std::move(rhs));
2883
    }
2884
  }
2885
2886
  template <class U = T,
2887
            detail::enable_if_t<
2888
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2889
  void assign(expected_operations_base&& rhs) {
2890
    if (!this->m_has_val && rhs.m_has_val) {
2891
      auto tmp = std::move(geterr());
2892
      geterr().~unexpected<E>();
2893
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2894
      try {
2895
        construct(std::move(rhs).get());
2896
      } catch (...) {
2897
        geterr() = std::move(tmp);
2898
        throw;
2899
      }
2900
#else
2901
      construct(std::move(rhs).get());
2902
#endif
2903
    } else {
2904
      assign_common(std::move(rhs));
2905
    }
2906
  }
2907
2908
#else
2909
2910
  // If exceptions are disabled then we can just copy-construct
2911
  void assign(const expected_operations_base& rhs) noexcept {
2912
    if (!this->m_has_val && rhs.m_has_val) {
2913
      geterr().~unexpected<E>();
2914
      construct(rhs.get());
2915
    } else {
2916
      assign_common(rhs);
2917
    }
2918
  }
2919
2920
  void assign(expected_operations_base&& rhs) noexcept {
2921
    if (!this->m_has_val && rhs.m_has_val) {
2922
      geterr().~unexpected<E>();
2923
      construct(std::move(rhs).get());
2924
    } else {
2925
      assign_common(std::move(rhs));
2926
    }
2927
  }
2928
2929
#endif
2930
2931
  // The common part of move/copy assigning
2932
  template <class Rhs>
2933
  void assign_common(Rhs&& rhs) {
2934
    if (this->m_has_val) {
2935
      if (rhs.m_has_val) {
2936
        get() = std::forward<Rhs>(rhs).get();
2937
      } else {
2938
        destroy_val();
2939
        construct_error(std::forward<Rhs>(rhs).geterr());
2940
      }
2941
    } else {
2942
      if (!rhs.m_has_val) {
2943
        geterr() = std::forward<Rhs>(rhs).geterr();
2944
      }
2945
    }
2946
  }
2947
2948
0
  bool has_value() const { return this->m_has_val; }
2949
2950
  TL_EXPECTED_11_CONSTEXPR T& get() & { return this->m_val; }
2951
0
  constexpr const T& get() const& { return this->m_val; }
2952
  TL_EXPECTED_11_CONSTEXPR T&& get() && { return std::move(this->m_val); }
2953
#ifndef TL_EXPECTED_NO_CONSTRR
2954
  constexpr const T&& get() const&& { return std::move(this->m_val); }
2955
#endif
2956
2957
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2958
    return this->m_unexpect;
2959
  }
2960
0
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2961
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2962
    return std::move(this->m_unexpect);
2963
  }
2964
#ifndef TL_EXPECTED_NO_CONSTRR
2965
  constexpr const unexpected<E>&& geterr() const&& {
2966
    return std::move(this->m_unexpect);
2967
  }
2968
#endif
2969
2970
  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2971
};
2972
2973
// This base class provides some handy member functions which can be used in
2974
// further derived classes
2975
template <class E>
2976
struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2977
  using expected_storage_base<void, E>::expected_storage_base;
2978
2979
  template <class... Args>
2980
  void construct() noexcept {
2981
    this->m_has_val = true;
2982
  }
2983
2984
  // This function doesn't use its argument, but needs it so that code in
2985
  // levels above this can work independently of whether T is void
2986
  template <class Rhs>
2987
  void construct_with(Rhs&&) noexcept {
2988
    this->m_has_val = true;
2989
  }
2990
2991
  template <class... Args>
2992
  void construct_error(Args&&... args) noexcept {
2993
    new (std::addressof(this->m_unexpect))
2994
        unexpected<E>(std::forward<Args>(args)...);
2995
    this->m_has_val = false;
2996
  }
2997
2998
  template <class Rhs>
2999
  void assign(Rhs&& rhs) noexcept {
3000
    if (!this->m_has_val) {
3001
      if (rhs.m_has_val) {
3002
        geterr().~unexpected<E>();
3003
        construct();
3004
      } else {
3005
        geterr() = std::forward<Rhs>(rhs).geterr();
3006
      }
3007
    } else {
3008
      if (!rhs.m_has_val) {
3009
        construct_error(std::forward<Rhs>(rhs).geterr());
3010
      }
3011
    }
3012
  }
3013
3014
  bool has_value() const { return this->m_has_val; }
3015
3016
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
3017
    return this->m_unexpect;
3018
  }
3019
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
3020
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
3021
    return std::move(this->m_unexpect);
3022
  }
3023
#ifndef TL_EXPECTED_NO_CONSTRR
3024
  constexpr const unexpected<E>&& geterr() const&& {
3025
    return std::move(this->m_unexpect);
3026
  }
3027
#endif
3028
3029
  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
3030
    // no-op
3031
  }
3032
};
3033
3034
// This class manages conditionally having a trivial copy constructor
3035
// This specialization is for when T and E are trivially copy constructible
3036
template <class T, class E,
3037
          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(
3038
                                   T)>::value &&
3039
                 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
3040
struct expected_copy_base : expected_operations_base<T, E> {
3041
  using expected_operations_base<T, E>::expected_operations_base;
3042
};
3043
3044
// This specialization is for when T or E are not trivially copy constructible
3045
template <class T, class E>
3046
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
3047
  using expected_operations_base<T, E>::expected_operations_base;
3048
3049
  expected_copy_base() = default;
3050
  expected_copy_base(const expected_copy_base& rhs)
3051
0
      : expected_operations_base<T, E>(no_init) {
3052
0
    if (rhs.has_value()) {
3053
0
      this->construct_with(rhs);
3054
0
    } else {
3055
0
      this->construct_error(rhs.geterr());
3056
0
    }
3057
0
  }
3058
3059
  expected_copy_base(expected_copy_base&& rhs) = default;
3060
  expected_copy_base& operator=(const expected_copy_base& rhs) = default;
3061
  expected_copy_base& operator=(expected_copy_base&& rhs) = default;
3062
};
3063
3064
// This class manages conditionally having a trivial move constructor
3065
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3066
// doesn't implement an analogue to std::is_trivially_move_constructible. We
3067
// have to make do with a non-trivial move constructor even if T is trivially
3068
// move constructible
3069
#ifndef TL_EXPECTED_GCC49
3070
template <class T, class E,
3071
          bool =
3072
              is_void_or<T, std::is_trivially_move_constructible<T>>::value &&
3073
              std::is_trivially_move_constructible<E>::value>
3074
struct expected_move_base : expected_copy_base<T, E> {
3075
  using expected_copy_base<T, E>::expected_copy_base;
3076
};
3077
#else
3078
template <class T, class E, bool = false>
3079
struct expected_move_base;
3080
#endif
3081
template <class T, class E>
3082
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
3083
  using expected_copy_base<T, E>::expected_copy_base;
3084
3085
  expected_move_base() = default;
3086
0
  expected_move_base(const expected_move_base& rhs) = default;
3087
3088
  expected_move_base(expected_move_base&& rhs) noexcept(
3089
      std::is_nothrow_move_constructible<T>::value)
3090
      : expected_copy_base<T, E>(no_init) {
3091
    if (rhs.has_value()) {
3092
      this->construct_with(std::move(rhs));
3093
    } else {
3094
      this->construct_error(std::move(rhs.geterr()));
3095
    }
3096
  }
3097
  expected_move_base& operator=(const expected_move_base& rhs) = default;
3098
  expected_move_base& operator=(expected_move_base&& rhs) = default;
3099
};
3100
3101
// This class manages conditionally having a trivial copy assignment operator
3102
template <
3103
    class T, class E,
3104
    bool =
3105
        is_void_or<
3106
            T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
3107
                           TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
3108
                           TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value &&
3109
        TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value &&
3110
        TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value &&
3111
        TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
3112
struct expected_copy_assign_base : expected_move_base<T, E> {
3113
  using expected_move_base<T, E>::expected_move_base;
3114
};
3115
3116
template <class T, class E>
3117
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
3118
  using expected_move_base<T, E>::expected_move_base;
3119
3120
  expected_copy_assign_base() = default;
3121
0
  expected_copy_assign_base(const expected_copy_assign_base& rhs) = default;
3122
3123
  expected_copy_assign_base(expected_copy_assign_base&& rhs) = default;
3124
  expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
3125
    this->assign(rhs);
3126
    return *this;
3127
  }
3128
  expected_copy_assign_base& operator=(expected_copy_assign_base&& rhs) =
3129
      default;
3130
};
3131
3132
// This class manages conditionally having a trivial move assignment operator
3133
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3134
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
3135
// to make do with a non-trivial move assignment operator even if T is trivially
3136
// move assignable
3137
#ifndef TL_EXPECTED_GCC49
3138
template <
3139
    class T, class E,
3140
    bool = is_void_or<
3141
               T, conjunction<std::is_trivially_destructible<T>,
3142
                              std::is_trivially_move_constructible<T>,
3143
                              std::is_trivially_move_assignable<T>>>::value &&
3144
           std::is_trivially_destructible<E>::value &&
3145
           std::is_trivially_move_constructible<E>::value &&
3146
           std::is_trivially_move_assignable<E>::value>
3147
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
3148
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3149
};
3150
#else
3151
template <class T, class E, bool = false>
3152
struct expected_move_assign_base;
3153
#endif
3154
3155
template <class T, class E>
3156
struct expected_move_assign_base<T, E, false>
3157
    : expected_copy_assign_base<T, E> {
3158
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3159
3160
  expected_move_assign_base() = default;
3161
0
  expected_move_assign_base(const expected_move_assign_base& rhs) = default;
3162
3163
  expected_move_assign_base(expected_move_assign_base&& rhs) = default;
3164
3165
  expected_move_assign_base& operator=(const expected_move_assign_base& rhs) =
3166
      default;
3167
3168
  expected_move_assign_base&
3169
  operator=(expected_move_assign_base&& rhs) noexcept(
3170
      std::is_nothrow_move_constructible<T>::value &&
3171
      std::is_nothrow_move_assignable<T>::value) {
3172
    this->assign(std::move(rhs));
3173
    return *this;
3174
  }
3175
};
3176
3177
// expected_delete_ctor_base will conditionally delete copy and move
3178
// constructors depending on whether T is copy/move constructible
3179
template <class T, class E,
3180
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3181
                             std::is_copy_constructible<E>::value),
3182
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3183
                             std::is_move_constructible<E>::value)>
3184
struct expected_delete_ctor_base {
3185
  expected_delete_ctor_base() = default;
3186
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3187
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3188
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3189
      default;
3190
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3191
      default;
3192
};
3193
3194
template <class T, class E>
3195
struct expected_delete_ctor_base<T, E, true, false> {
3196
  expected_delete_ctor_base() = default;
3197
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3198
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3199
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3200
      default;
3201
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3202
      default;
3203
};
3204
3205
template <class T, class E>
3206
struct expected_delete_ctor_base<T, E, false, true> {
3207
  expected_delete_ctor_base() = default;
3208
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3209
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3210
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3211
      default;
3212
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3213
      default;
3214
};
3215
3216
template <class T, class E>
3217
struct expected_delete_ctor_base<T, E, false, false> {
3218
  expected_delete_ctor_base() = default;
3219
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3220
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3221
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3222
      default;
3223
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3224
      default;
3225
};
3226
3227
// expected_delete_assign_base will conditionally delete copy and move
3228
// constructors depending on whether T and E are copy/move constructible +
3229
// assignable
3230
template <class T, class E,
3231
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3232
                             std::is_copy_constructible<E>::value &&
3233
                             is_copy_assignable_or_void<T>::value &&
3234
                             std::is_copy_assignable<E>::value),
3235
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3236
                             std::is_move_constructible<E>::value &&
3237
                             is_move_assignable_or_void<T>::value &&
3238
                             std::is_move_assignable<E>::value)>
3239
struct expected_delete_assign_base {
3240
  expected_delete_assign_base() = default;
3241
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3242
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3243
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3244
      default;
3245
  expected_delete_assign_base& operator=(
3246
      expected_delete_assign_base&&) noexcept = default;
3247
};
3248
3249
template <class T, class E>
3250
struct expected_delete_assign_base<T, E, true, false> {
3251
  expected_delete_assign_base() = default;
3252
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3253
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3254
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3255
      default;
3256
  expected_delete_assign_base& operator=(
3257
      expected_delete_assign_base&&) noexcept = delete;
3258
};
3259
3260
template <class T, class E>
3261
struct expected_delete_assign_base<T, E, false, true> {
3262
  expected_delete_assign_base() = default;
3263
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3264
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3265
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3266
      delete;
3267
  expected_delete_assign_base& operator=(
3268
      expected_delete_assign_base&&) noexcept = default;
3269
};
3270
3271
template <class T, class E>
3272
struct expected_delete_assign_base<T, E, false, false> {
3273
  expected_delete_assign_base() = default;
3274
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3275
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3276
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3277
      delete;
3278
  expected_delete_assign_base& operator=(
3279
      expected_delete_assign_base&&) noexcept = delete;
3280
};
3281
3282
// This is needed to be able to construct the expected_default_ctor_base which
3283
// follows, while still conditionally deleting the default constructor.
3284
struct default_constructor_tag {
3285
  explicit constexpr default_constructor_tag() = default;
3286
};
3287
3288
// expected_default_ctor_base will ensure that expected has a deleted default
3289
// consturctor if T is not default constructible.
3290
// This specialization is for when T is default constructible
3291
template <class T, class E,
3292
          bool Enable =
3293
              std::is_default_constructible<T>::value || std::is_void<T>::value>
3294
struct expected_default_ctor_base {
3295
  constexpr expected_default_ctor_base() noexcept = default;
3296
  constexpr expected_default_ctor_base(
3297
      expected_default_ctor_base const&) noexcept = default;
3298
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3299
      default;
3300
  expected_default_ctor_base& operator=(
3301
      expected_default_ctor_base const&) noexcept = default;
3302
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3303
      default;
3304
3305
13.4k
  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
3305
13.4k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_pattern_init, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_pattern_list_detail::compiled_routes, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
3306
};
3307
3308
// This specialization is for when T is not default constructible
3309
template <class T, class E>
3310
struct expected_default_ctor_base<T, E, false> {
3311
  constexpr expected_default_ctor_base() noexcept = delete;
3312
  constexpr expected_default_ctor_base(
3313
      expected_default_ctor_base const&) noexcept = default;
3314
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3315
      default;
3316
  expected_default_ctor_base& operator=(
3317
      expected_default_ctor_base const&) noexcept = default;
3318
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3319
      default;
3320
3321
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3322
};
3323
}  // namespace detail
3324
3325
template <class E>
3326
class bad_expected_access : public std::exception {
3327
 public:
3328
0
  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3329
3330
0
  virtual const char* what() const noexcept override {
3331
0
    return "Bad expected access";
3332
0
  }
3333
3334
  const E& error() const& { return m_val; }
3335
  E& error() & { return m_val; }
3336
  const E&& error() const&& { return std::move(m_val); }
3337
  E&& error() && { return std::move(m_val); }
3338
3339
 private:
3340
  E m_val;
3341
};
3342
3343
/// An `expected<T, E>` object is an object that contains the storage for
3344
/// another object and manages the lifetime of this contained object `T`.
3345
/// Alternatively it could contain the storage for another unexpected object
3346
/// `E`. The contained object may not be initialized after the expected object
3347
/// has been initialized, and may not be destroyed before the expected object
3348
/// has been destroyed. The initialization state of the contained object is
3349
/// tracked by the expected object.
3350
template <class T, class E>
3351
class expected : private detail::expected_move_assign_base<T, E>,
3352
                 private detail::expected_delete_ctor_base<T, E>,
3353
                 private detail::expected_delete_assign_base<T, E>,
3354
                 private detail::expected_default_ctor_base<T, E> {
3355
  static_assert(!std::is_reference<T>::value, "T must not be a reference");
3356
  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3357
                "T must not be in_place_t");
3358
  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3359
                "T must not be unexpect_t");
3360
  static_assert(
3361
      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3362
      "T must not be unexpected<E>");
3363
  static_assert(!std::is_reference<E>::value, "E must not be a reference");
3364
3365
2.80k
  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
3365
2.80k
  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()
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::valptr()
3366
  const T* valptr() const { return std::addressof(this->m_val); }
3367
  unexpected<E>* errptr() { return std::addressof(this->m_unexpect); }
3368
  const unexpected<E>* errptr() const {
3369
    return std::addressof(this->m_unexpect);
3370
  }
3371
3372
  template <class U = T,
3373
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3374
0
  TL_EXPECTED_11_CONSTEXPR U& val() {
3375
0
    return this->m_val;
3376
0
  }
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEE3valIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZN2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEE3valIS3_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS9_v
3377
0
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::err()
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::err()
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::err()
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::err()
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::err()
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::err()
3378
3379
  template <class U = T,
3380
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3381
  constexpr const U& val() const {
3382
    return this->m_val;
3383
  }
3384
  constexpr const unexpected<E>& err() const { return this->m_unexpect; }
3385
3386
  using impl_base = detail::expected_move_assign_base<T, E>;
3387
  using ctor_base = detail::expected_default_ctor_base<T, E>;
3388
3389
 public:
3390
  typedef T value_type;
3391
  typedef E error_type;
3392
  typedef unexpected<E> unexpected_type;
3393
3394
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3395
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3396
  template <class F>
3397
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & {
3398
    return and_then_impl(*this, std::forward<F>(f));
3399
  }
3400
  template <class F>
3401
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && {
3402
    return and_then_impl(std::move(*this), std::forward<F>(f));
3403
  }
3404
  template <class F>
3405
  constexpr auto and_then(F&& f) const& {
3406
    return and_then_impl(*this, std::forward<F>(f));
3407
  }
3408
3409
#ifndef TL_EXPECTED_NO_CONSTRR
3410
  template <class F>
3411
  constexpr auto and_then(F&& f) const&& {
3412
    return and_then_impl(std::move(*this), std::forward<F>(f));
3413
  }
3414
#endif
3415
3416
#else
3417
  template <class F>
3418
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & -> decltype(and_then_impl(
3419
      std::declval<expected&>(), std::forward<F>(f))) {
3420
    return and_then_impl(*this, std::forward<F>(f));
3421
  }
3422
  template <class F>
3423
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && -> decltype(and_then_impl(
3424
      std::declval<expected&&>(), std::forward<F>(f))) {
3425
    return and_then_impl(std::move(*this), std::forward<F>(f));
3426
  }
3427
  template <class F>
3428
  constexpr auto and_then(F&& f) const& -> decltype(and_then_impl(
3429
      std::declval<expected const&>(), std::forward<F>(f))) {
3430
    return and_then_impl(*this, std::forward<F>(f));
3431
  }
3432
3433
#ifndef TL_EXPECTED_NO_CONSTRR
3434
  template <class F>
3435
  constexpr auto and_then(F&& f) const&& -> decltype(and_then_impl(
3436
      std::declval<expected const&&>(), std::forward<F>(f))) {
3437
    return and_then_impl(std::move(*this), std::forward<F>(f));
3438
  }
3439
#endif
3440
#endif
3441
3442
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3443
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3444
  template <class F>
3445
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) & {
3446
    return expected_map_impl(*this, std::forward<F>(f));
3447
  }
3448
  template <class F>
3449
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) && {
3450
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3451
  }
3452
  template <class F>
3453
  constexpr auto map(F&& f) const& {
3454
    return expected_map_impl(*this, std::forward<F>(f));
3455
  }
3456
  template <class F>
3457
  constexpr auto map(F&& f) const&& {
3458
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3459
  }
3460
#else
3461
  template <class F>
3462
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3463
      std::declval<expected&>(), std::declval<F&&>())) map(F&& f) & {
3464
    return expected_map_impl(*this, std::forward<F>(f));
3465
  }
3466
  template <class F>
3467
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3468
                                                      std::declval<F&&>()))
3469
  map(F&& f) && {
3470
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3471
  }
3472
  template <class F>
3473
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3474
                                       std::declval<F&&>()))
3475
  map(F&& f) const& {
3476
    return expected_map_impl(*this, std::forward<F>(f));
3477
  }
3478
3479
#ifndef TL_EXPECTED_NO_CONSTRR
3480
  template <class F>
3481
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3482
                                       std::declval<F&&>())) map(F&& f)
3483
      const&& {
3484
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3485
  }
3486
#endif
3487
#endif
3488
3489
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3490
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3491
  template <class F>
3492
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) & {
3493
    return expected_map_impl(*this, std::forward<F>(f));
3494
  }
3495
  template <class F>
3496
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) && {
3497
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3498
  }
3499
  template <class F>
3500
  constexpr auto transform(F&& f) const& {
3501
    return expected_map_impl(*this, std::forward<F>(f));
3502
  }
3503
  template <class F>
3504
  constexpr auto transform(F&& f) const&& {
3505
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3506
  }
3507
#else
3508
  template <class F>
3509
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3510
      std::declval<expected&>(), std::declval<F&&>())) transform(F&& f) & {
3511
    return expected_map_impl(*this, std::forward<F>(f));
3512
  }
3513
  template <class F>
3514
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3515
                                                      std::declval<F&&>()))
3516
  transform(F&& f) && {
3517
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3518
  }
3519
  template <class F>
3520
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3521
                                       std::declval<F&&>()))
3522
  transform(F&& f) const& {
3523
    return expected_map_impl(*this, std::forward<F>(f));
3524
  }
3525
3526
#ifndef TL_EXPECTED_NO_CONSTRR
3527
  template <class F>
3528
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3529
                                       std::declval<F&&>())) transform(F&& f)
3530
      const&& {
3531
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3532
  }
3533
#endif
3534
#endif
3535
3536
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3537
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3538
  template <class F>
3539
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) & {
3540
    return map_error_impl(*this, std::forward<F>(f));
3541
  }
3542
  template <class F>
3543
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) && {
3544
    return map_error_impl(std::move(*this), std::forward<F>(f));
3545
  }
3546
  template <class F>
3547
  constexpr auto map_error(F&& f) const& {
3548
    return map_error_impl(*this, std::forward<F>(f));
3549
  }
3550
  template <class F>
3551
  constexpr auto map_error(F&& f) const&& {
3552
    return map_error_impl(std::move(*this), std::forward<F>(f));
3553
  }
3554
#else
3555
  template <class F>
3556
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3557
      std::declval<expected&>(), std::declval<F&&>())) map_error(F&& f) & {
3558
    return map_error_impl(*this, std::forward<F>(f));
3559
  }
3560
  template <class F>
3561
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3562
                                                   std::declval<F&&>()))
3563
  map_error(F&& f) && {
3564
    return map_error_impl(std::move(*this), std::forward<F>(f));
3565
  }
3566
  template <class F>
3567
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3568
                                    std::declval<F&&>()))
3569
  map_error(F&& f) const& {
3570
    return map_error_impl(*this, std::forward<F>(f));
3571
  }
3572
3573
#ifndef TL_EXPECTED_NO_CONSTRR
3574
  template <class F>
3575
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3576
                                    std::declval<F&&>())) map_error(F&& f)
3577
      const&& {
3578
    return map_error_impl(std::move(*this), std::forward<F>(f));
3579
  }
3580
#endif
3581
#endif
3582
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3583
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3584
  template <class F>
3585
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) & {
3586
    return map_error_impl(*this, std::forward<F>(f));
3587
  }
3588
  template <class F>
3589
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) && {
3590
    return map_error_impl(std::move(*this), std::forward<F>(f));
3591
  }
3592
  template <class F>
3593
  constexpr auto transform_error(F&& f) const& {
3594
    return map_error_impl(*this, std::forward<F>(f));
3595
  }
3596
  template <class F>
3597
  constexpr auto transform_error(F&& f) const&& {
3598
    return map_error_impl(std::move(*this), std::forward<F>(f));
3599
  }
3600
#else
3601
  template <class F>
3602
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3603
      std::declval<expected&>(),
3604
      std::declval<F&&>())) transform_error(F&& f) & {
3605
    return map_error_impl(*this, std::forward<F>(f));
3606
  }
3607
  template <class F>
3608
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3609
                                                   std::declval<F&&>()))
3610
  transform_error(F&& f) && {
3611
    return map_error_impl(std::move(*this), std::forward<F>(f));
3612
  }
3613
  template <class F>
3614
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3615
                                    std::declval<F&&>()))
3616
  transform_error(F&& f) const& {
3617
    return map_error_impl(*this, std::forward<F>(f));
3618
  }
3619
3620
#ifndef TL_EXPECTED_NO_CONSTRR
3621
  template <class F>
3622
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3623
                                    std::declval<F&&>())) transform_error(F&& f)
3624
      const&& {
3625
    return map_error_impl(std::move(*this), std::forward<F>(f));
3626
  }
3627
#endif
3628
#endif
3629
  template <class F>
3630
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) & {
3631
    return or_else_impl(*this, std::forward<F>(f));
3632
  }
3633
3634
  template <class F>
3635
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) && {
3636
    return or_else_impl(std::move(*this), std::forward<F>(f));
3637
  }
3638
3639
  template <class F>
3640
  expected constexpr or_else(F&& f) const& {
3641
    return or_else_impl(*this, std::forward<F>(f));
3642
  }
3643
3644
#ifndef TL_EXPECTED_NO_CONSTRR
3645
  template <class F>
3646
  expected constexpr or_else(F&& f) const&& {
3647
    return or_else_impl(std::move(*this), std::forward<F>(f));
3648
  }
3649
#endif
3650
  constexpr expected() = default;
3651
0
  constexpr expected(const expected& rhs) = default;
3652
  constexpr expected(expected&& rhs) = default;
3653
  expected& operator=(const expected& rhs) = default;
3654
  expected& operator=(expected&& rhs) = default;
3655
3656
  template <class... Args,
3657
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
3658
                nullptr>
3659
  constexpr expected(in_place_t, Args&&... args)
3660
2.52k
      : impl_base(in_place, std::forward<Args>(args)...),
3661
2.52k
        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
3660
2.52k
      : impl_base(in_place, std::forward<Args>(args)...),
3661
2.52k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA1_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA2_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IJRS8_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Unexecuted instantiation: _ZN2tl8expectedIN3ada17url_search_paramsENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS6_IS8_EEEEN3ada6errorsEEC2IJSA_ETnPNS1_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE0EEENS1_6errorsEEC2IJS9_ETnPNS3_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE1EEENS1_6errorsEEC2IJS9_ETnPNS3_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__14pairINS3_17basic_string_viewIcNS3_11char_traitsIcEEEES8_EELNS1_27url_search_params_iter_typeE2EEENS1_6errorsEEC2IJSB_ETnPNS3_9enable_ifIXsr3std16is_constructibleISB_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IJRS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
3662
3663
  template <class U, class... Args,
3664
            detail::enable_if_t<std::is_constructible<
3665
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3666
  constexpr expected(in_place_t, std::initializer_list<U> il, Args&&... args)
3667
      : impl_base(in_place, il, std::forward<Args>(args)...),
3668
        ctor_base(detail::default_constructor_tag{}) {}
3669
3670
  template <
3671
      class G = E,
3672
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3673
      detail::enable_if_t<!std::is_convertible<const G&, E>::value>* = nullptr>
3674
  explicit constexpr expected(const unexpected<G>& e)
3675
      : impl_base(unexpect, e.value()),
3676
        ctor_base(detail::default_constructor_tag{}) {}
3677
3678
  template <
3679
      class G = E,
3680
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3681
      detail::enable_if_t<std::is_convertible<const G&, E>::value>* = nullptr>
3682
  constexpr expected(unexpected<G> const& e)
3683
      : impl_base(unexpect, e.value()),
3684
        ctor_base(detail::default_constructor_tag{}) {}
3685
3686
  template <
3687
      class G = E,
3688
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3689
      detail::enable_if_t<!std::is_convertible<G&&, E>::value>* = nullptr>
3690
  explicit constexpr expected(unexpected<G>&& e) noexcept(
3691
      std::is_nothrow_constructible<E, G&&>::value)
3692
      : impl_base(unexpect, std::move(e.value())),
3693
        ctor_base(detail::default_constructor_tag{}) {}
3694
3695
  template <
3696
      class G = E,
3697
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3698
      detail::enable_if_t<std::is_convertible<G&&, E>::value>* = nullptr>
3699
  constexpr expected(unexpected<G>&& e) noexcept(
3700
      std::is_nothrow_constructible<E, G&&>::value)
3701
10.8k
      : impl_base(unexpect, std::move(e.value())),
3702
10.8k
        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
3701
10.8k
      : impl_base(unexpect, std::move(e.value())),
3702
10.8k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Unexecuted instantiation: _ZN2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEEC2IS4_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_OT_EE5valueEvE4typeELPv0ETnPNS8_IXsr3std14is_convertibleISA_S4_EE5valueEvE4typeELSE_0EEEONS_10unexpectedIS9_EE
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IS8_TnPNS1_9enable_ifIXsr3std16is_constructibleIS8_OT_EE5valueEvE4typeELPv0ETnPNSB_IXsr3std14is_convertibleISD_S8_EE5valueEvE4typeELSH_0EEEONS_10unexpectedISC_EE
3703
3704
  template <class... Args,
3705
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
3706
                nullptr>
3707
  constexpr explicit expected(unexpect_t, Args&&... args)
3708
      : impl_base(unexpect, std::forward<Args>(args)...),
3709
        ctor_base(detail::default_constructor_tag{}) {}
3710
3711
  template <class U, class... Args,
3712
            detail::enable_if_t<std::is_constructible<
3713
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3714
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3715
                              Args&&... args)
3716
      : impl_base(unexpect, il, std::forward<Args>(args)...),
3717
        ctor_base(detail::default_constructor_tag{}) {}
3718
3719
  template <class U, class G,
3720
            detail::enable_if_t<!(std::is_convertible<U const&, T>::value &&
3721
                                  std::is_convertible<G const&, E>::value)>* =
3722
                nullptr,
3723
            detail::expected_enable_from_other<T, E, U, G, const U&,
3724
                                               const G&>* = nullptr>
3725
  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3726
      : ctor_base(detail::default_constructor_tag{}) {
3727
    if (rhs.has_value()) {
3728
      this->construct(*rhs);
3729
    } else {
3730
      this->construct_error(rhs.error());
3731
    }
3732
  }
3733
3734
  template <
3735
      class U, class G,
3736
      detail::enable_if_t<(std::is_convertible<U const&, T>::value &&
3737
                           std::is_convertible<G const&, E>::value)>* = nullptr,
3738
      detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* =
3739
          nullptr>
3740
  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3741
      : ctor_base(detail::default_constructor_tag{}) {
3742
    if (rhs.has_value()) {
3743
      this->construct(*rhs);
3744
    } else {
3745
      this->construct_error(rhs.error());
3746
    }
3747
  }
3748
3749
  template <
3750
      class U, class G,
3751
      detail::enable_if_t<!(std::is_convertible<U&&, T>::value &&
3752
                            std::is_convertible<G&&, E>::value)>* = nullptr,
3753
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3754
  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3755
      : ctor_base(detail::default_constructor_tag{}) {
3756
    if (rhs.has_value()) {
3757
      this->construct(std::move(*rhs));
3758
    } else {
3759
      this->construct_error(std::move(rhs.error()));
3760
    }
3761
  }
3762
3763
  template <
3764
      class U, class G,
3765
      detail::enable_if_t<(std::is_convertible<U&&, T>::value &&
3766
                           std::is_convertible<G&&, E>::value)>* = nullptr,
3767
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3768
  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3769
      : ctor_base(detail::default_constructor_tag{}) {
3770
    if (rhs.has_value()) {
3771
      this->construct(std::move(*rhs));
3772
    } else {
3773
      this->construct_error(std::move(rhs.error()));
3774
    }
3775
  }
3776
3777
  template <class U = T,
3778
            detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr,
3779
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3780
  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3781
      : expected(in_place, std::forward<U>(v)) {}
3782
3783
  template <class U = T,
3784
            detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr,
3785
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3786
  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3787
2.52k
      : 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
3787
2.52k
      : expected(in_place, std::forward<U>(v)) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS7_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS7_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESL_EE5valueEvE4typeELSI_0EEESE_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IRA1_KcTnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleIS7_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SO_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESO_EE5valueEvE4typeELSL_0EEESH_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IRA2_KcTnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleIS7_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SO_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESO_EE5valueEvE4typeELSL_0EEESH_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IRS8_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S8_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS8_SF_EE5valuentsr3std7is_sameINS1_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SM_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESM_EE5valueEvE4typeELSJ_0EEESF_
Unexecuted instantiation: _ZN2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S3_EE5valueEvE4typeELPv0ETnPNS8_IXaaaaaasr3std16is_constructibleIS3_SA_EE5valuentsr3std7is_sameINS7_5decayIS9_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS5_SH_EE5valuentsr3std7is_sameINS_10unexpectedIS4_EESH_EE5valueEvE4typeELSE_0EEESA_
Unexecuted instantiation: _ZN2tl8expectedIN3ada17url_search_paramsENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS6_IS8_EEEEN3ada6errorsEEC2ISA_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_SA_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleISA_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISD_SO_EE5valuentsr3std7is_sameINS_10unexpectedISC_EESO_EE5valueEvE4typeELSL_0EEESH_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE0EEENS1_6errorsEEC2IS9_TnPNS3_9enable_ifIXsr3std14is_convertibleIOT_S9_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS9_SF_EE5valuentsr3std7is_sameINS3_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISB_SM_EE5valuentsr3std7is_sameINS_10unexpectedISA_EESM_EE5valueEvE4typeELSJ_0EEESF_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE1EEENS1_6errorsEEC2IS9_TnPNS3_9enable_ifIXsr3std14is_convertibleIOT_S9_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS9_SF_EE5valuentsr3std7is_sameINS3_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISB_SM_EE5valuentsr3std7is_sameINS_10unexpectedISA_EESM_EE5valueEvE4typeELSJ_0EEESF_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__14pairINS3_17basic_string_viewIcNS3_11char_traitsIcEEEES8_EELNS1_27url_search_params_iter_typeE2EEENS1_6errorsEEC2ISB_TnPNS3_9enable_ifIXsr3std14is_convertibleIOT_SB_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleISB_SH_EE5valuentsr3std7is_sameINS3_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISD_SO_EE5valuentsr3std7is_sameINS_10unexpectedISC_EESO_EE5valueEvE4typeELSL_0EEESH_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IRS7_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS7_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS9_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS8_EESL_EE5valueEvE4typeELSI_0EEESE_
3788
3789
  template <
3790
      class U = T, class G = T,
3791
      detail::enable_if_t<std::is_nothrow_constructible<T, U&&>::value>* =
3792
          nullptr,
3793
      detail::enable_if_t<!std::is_void<G>::value>* = nullptr,
3794
      detail::enable_if_t<
3795
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3796
           !detail::conjunction<std::is_scalar<T>,
3797
                                std::is_same<T, detail::decay_t<U>>>::value &&
3798
           std::is_constructible<T, U>::value &&
3799
           std::is_assignable<G&, U>::value &&
3800
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3801
  expected& operator=(U&& v) {
3802
    if (has_value()) {
3803
      val() = std::forward<U>(v);
3804
    } else {
3805
      err().~unexpected<E>();
3806
      ::new (valptr()) T(std::forward<U>(v));
3807
      this->m_has_val = true;
3808
    }
3809
3810
    return *this;
3811
  }
3812
3813
  template <
3814
      class U = T, class G = T,
3815
      detail::enable_if_t<!std::is_nothrow_constructible<T, U&&>::value>* =
3816
          nullptr,
3817
      detail::enable_if_t<!std::is_void<U>::value>* = nullptr,
3818
      detail::enable_if_t<
3819
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3820
           !detail::conjunction<std::is_scalar<T>,
3821
                                std::is_same<T, detail::decay_t<U>>>::value &&
3822
           std::is_constructible<T, U>::value &&
3823
           std::is_assignable<G&, U>::value &&
3824
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3825
  expected& operator=(U&& v) {
3826
    if (has_value()) {
3827
      val() = std::forward<U>(v);
3828
    } else {
3829
      auto tmp = std::move(err());
3830
      err().~unexpected<E>();
3831
3832
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3833
      try {
3834
        ::new (valptr()) T(std::forward<U>(v));
3835
        this->m_has_val = true;
3836
      } catch (...) {
3837
        err() = std::move(tmp);
3838
        throw;
3839
      }
3840
#else
3841
      ::new (valptr()) T(std::forward<U>(v));
3842
      this->m_has_val = true;
3843
#endif
3844
    }
3845
3846
    return *this;
3847
  }
3848
3849
  template <class G = E,
3850
            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3851
                                std::is_assignable<G&, G>::value>* = nullptr>
3852
  expected& operator=(const unexpected<G>& rhs) {
3853
    if (!has_value()) {
3854
      err() = rhs;
3855
    } else {
3856
      this->destroy_val();
3857
      ::new (errptr()) unexpected<E>(rhs);
3858
      this->m_has_val = false;
3859
    }
3860
3861
    return *this;
3862
  }
3863
3864
  template <class G = E,
3865
            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3866
                                std::is_move_assignable<G>::value>* = nullptr>
3867
  expected& operator=(unexpected<G>&& rhs) noexcept {
3868
    if (!has_value()) {
3869
      err() = std::move(rhs);
3870
    } else {
3871
      this->destroy_val();
3872
      ::new (errptr()) unexpected<E>(std::move(rhs));
3873
      this->m_has_val = false;
3874
    }
3875
3876
    return *this;
3877
  }
3878
3879
  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3880
                               T, Args&&...>::value>* = nullptr>
3881
  void emplace(Args&&... args) {
3882
    if (has_value()) {
3883
      val().~T();
3884
    } else {
3885
      err().~unexpected<E>();
3886
      this->m_has_val = true;
3887
    }
3888
    ::new (valptr()) T(std::forward<Args>(args)...);
3889
  }
3890
3891
  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3892
                               T, Args&&...>::value>* = nullptr>
3893
  void emplace(Args&&... args) {
3894
    if (has_value()) {
3895
      val().~T();
3896
      ::new (valptr()) T(std::forward<Args>(args)...);
3897
    } else {
3898
      auto tmp = std::move(err());
3899
      err().~unexpected<E>();
3900
3901
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3902
      try {
3903
        ::new (valptr()) T(std::forward<Args>(args)...);
3904
        this->m_has_val = true;
3905
      } catch (...) {
3906
        err() = std::move(tmp);
3907
        throw;
3908
      }
3909
#else
3910
      ::new (valptr()) T(std::forward<Args>(args)...);
3911
      this->m_has_val = true;
3912
#endif
3913
    }
3914
  }
3915
3916
  template <class U, class... Args,
3917
            detail::enable_if_t<std::is_nothrow_constructible<
3918
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3919
  void emplace(std::initializer_list<U> il, Args&&... args) {
3920
    if (has_value()) {
3921
      T t(il, std::forward<Args>(args)...);
3922
      val() = std::move(t);
3923
    } else {
3924
      err().~unexpected<E>();
3925
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3926
      this->m_has_val = true;
3927
    }
3928
  }
3929
3930
  template <class U, class... Args,
3931
            detail::enable_if_t<!std::is_nothrow_constructible<
3932
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3933
  void emplace(std::initializer_list<U> il, Args&&... args) {
3934
    if (has_value()) {
3935
      T t(il, std::forward<Args>(args)...);
3936
      val() = std::move(t);
3937
    } else {
3938
      auto tmp = std::move(err());
3939
      err().~unexpected<E>();
3940
3941
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3942
      try {
3943
        ::new (valptr()) T(il, std::forward<Args>(args)...);
3944
        this->m_has_val = true;
3945
      } catch (...) {
3946
        err() = std::move(tmp);
3947
        throw;
3948
      }
3949
#else
3950
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3951
      this->m_has_val = true;
3952
#endif
3953
    }
3954
  }
3955
3956
 private:
3957
  using t_is_void = std::true_type;
3958
  using t_is_not_void = std::false_type;
3959
  using t_is_nothrow_move_constructible = std::true_type;
3960
  using move_constructing_t_can_throw = std::false_type;
3961
  using e_is_nothrow_move_constructible = std::true_type;
3962
  using move_constructing_e_can_throw = std::false_type;
3963
3964
  void swap_where_both_have_value(expected& /*rhs*/, t_is_void) noexcept {
3965
    // swapping void is a no-op
3966
  }
3967
3968
  void swap_where_both_have_value(expected& rhs, t_is_not_void) {
3969
    using std::swap;
3970
    swap(val(), rhs.val());
3971
  }
3972
3973
  void swap_where_only_one_has_value(expected& rhs, t_is_void) noexcept(
3974
      std::is_nothrow_move_constructible<E>::value) {
3975
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3976
    rhs.err().~unexpected_type();
3977
    std::swap(this->m_has_val, rhs.m_has_val);
3978
  }
3979
3980
  void swap_where_only_one_has_value(expected& rhs, t_is_not_void) {
3981
    swap_where_only_one_has_value_and_t_is_not_void(
3982
        rhs, typename std::is_nothrow_move_constructible<T>::type{},
3983
        typename std::is_nothrow_move_constructible<E>::type{});
3984
  }
3985
3986
  void swap_where_only_one_has_value_and_t_is_not_void(
3987
      expected& rhs, t_is_nothrow_move_constructible,
3988
      e_is_nothrow_move_constructible) noexcept {
3989
    auto temp = std::move(val());
3990
    val().~T();
3991
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3992
    rhs.err().~unexpected_type();
3993
    ::new (rhs.valptr()) T(std::move(temp));
3994
    std::swap(this->m_has_val, rhs.m_has_val);
3995
  }
3996
3997
  void swap_where_only_one_has_value_and_t_is_not_void(
3998
      expected& rhs, t_is_nothrow_move_constructible,
3999
      move_constructing_e_can_throw) {
4000
    auto temp = std::move(val());
4001
    val().~T();
4002
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
4003
    try {
4004
      ::new (errptr()) unexpected_type(std::move(rhs.err()));
4005
      rhs.err().~unexpected_type();
4006
      ::new (rhs.valptr()) T(std::move(temp));
4007
      std::swap(this->m_has_val, rhs.m_has_val);
4008
    } catch (...) {
4009
      val() = std::move(temp);
4010
      throw;
4011
    }
4012
#else
4013
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
4014
    rhs.err().~unexpected_type();
4015
    ::new (rhs.valptr()) T(std::move(temp));
4016
    std::swap(this->m_has_val, rhs.m_has_val);
4017
#endif
4018
  }
4019
4020
  void swap_where_only_one_has_value_and_t_is_not_void(
4021
      expected& rhs, move_constructing_t_can_throw,
4022
      e_is_nothrow_move_constructible) {
4023
    auto temp = std::move(rhs.err());
4024
    rhs.err().~unexpected_type();
4025
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
4026
    try {
4027
      ::new (rhs.valptr()) T(std::move(val()));
4028
      val().~T();
4029
      ::new (errptr()) unexpected_type(std::move(temp));
4030
      std::swap(this->m_has_val, rhs.m_has_val);
4031
    } catch (...) {
4032
      rhs.err() = std::move(temp);
4033
      throw;
4034
    }
4035
#else
4036
    ::new (rhs.valptr()) T(std::move(val()));
4037
    val().~T();
4038
    ::new (errptr()) unexpected_type(std::move(temp));
4039
    std::swap(this->m_has_val, rhs.m_has_val);
4040
#endif
4041
  }
4042
4043
 public:
4044
  template <class OT = T, class OE = E>
4045
  detail::enable_if_t<detail::is_swappable<OT>::value &&
4046
                      detail::is_swappable<OE>::value &&
4047
                      (std::is_nothrow_move_constructible<OT>::value ||
4048
                       std::is_nothrow_move_constructible<OE>::value)>
4049
  swap(expected& rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
4050
                               detail::is_nothrow_swappable<T>::value &&
4051
                               std::is_nothrow_move_constructible<E>::value &&
4052
                               detail::is_nothrow_swappable<E>::value) {
4053
    if (has_value() && rhs.has_value()) {
4054
      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
4055
    } else if (!has_value() && rhs.has_value()) {
4056
      rhs.swap(*this);
4057
    } else if (has_value()) {
4058
      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
4059
    } else {
4060
      using std::swap;
4061
      swap(err(), rhs.err());
4062
    }
4063
  }
4064
4065
  constexpr const T* operator->() const {
4066
    TL_ASSERT(has_value());
4067
    return valptr();
4068
  }
4069
2.80k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4070
2.80k
    TL_ASSERT(has_value());
4071
2.80k
    return valptr();
4072
2.80k
  }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator->()
tl::expected<ada::url_aggregator, ada::errors>::operator->()
Line
Count
Source
4069
2.80k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4070
2.80k
    TL_ASSERT(has_value());
4071
2.80k
    return valptr();
4072
2.80k
  }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::operator->()
4073
4074
  template <class U = T,
4075
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4076
  constexpr const U& operator*() const& {
4077
    TL_ASSERT(has_value());
4078
    return val();
4079
  }
4080
  template <class U = T,
4081
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4082
0
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4083
0
    TL_ASSERT(has_value());
4084
0
    return val();
4085
0
  }
Unexecuted instantiation: _ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZNR2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZNR2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Unexecuted instantiation: _ZNR2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEdeIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZNR2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEEdeIS3_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS9_v
4086
  template <class U = T,
4087
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4088
  constexpr const U&& operator*() const&& {
4089
    TL_ASSERT(has_value());
4090
    return std::move(val());
4091
  }
4092
  template <class U = T,
4093
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4094
  TL_EXPECTED_11_CONSTEXPR U&& operator*() && {
4095
    TL_ASSERT(has_value());
4096
    return std::move(val());
4097
  }
4098
4099
2.80k
  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
4099
2.80k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::has_value() const
4100
13.4k
  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
4100
13.4k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::operator bool() const
4101
4102
  template <class U = T,
4103
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4104
  TL_EXPECTED_11_CONSTEXPR const U& value() const& {
4105
    if (!has_value())
4106
      detail::throw_exception(bad_expected_access<E>(err().value()));
4107
    return val();
4108
  }
4109
  template <class U = T,
4110
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4111
0
  TL_EXPECTED_11_CONSTEXPR U& value() & {
4112
0
    if (!has_value())
4113
0
      detail::throw_exception(bad_expected_access<E>(err().value()));
4114
0
    return val();
4115
0
  }
4116
  template <class U = T,
4117
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4118
  TL_EXPECTED_11_CONSTEXPR const U&& value() const&& {
4119
    if (!has_value())
4120
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4121
    return std::move(val());
4122
  }
4123
  template <class U = T,
4124
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4125
  TL_EXPECTED_11_CONSTEXPR U&& value() && {
4126
    if (!has_value())
4127
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4128
    return std::move(val());
4129
  }
4130
4131
  constexpr const E& error() const& {
4132
    TL_ASSERT(!has_value());
4133
    return err().value();
4134
  }
4135
0
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4136
0
    TL_ASSERT(!has_value());
4137
0
    return err().value();
4138
0
  }
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::error() &
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::error() &
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::error() &
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::error() &
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::error() &
4139
  constexpr const E&& error() const&& {
4140
    TL_ASSERT(!has_value());
4141
    return std::move(err().value());
4142
  }
4143
  TL_EXPECTED_11_CONSTEXPR E&& error() && {
4144
    TL_ASSERT(!has_value());
4145
    return std::move(err().value());
4146
  }
4147
4148
  template <class U>
4149
  constexpr T value_or(U&& v) const& {
4150
    static_assert(std::is_copy_constructible<T>::value &&
4151
                      std::is_convertible<U&&, T>::value,
4152
                  "T must be copy-constructible and convertible to from U&&");
4153
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
4154
  }
4155
  template <class U>
4156
  TL_EXPECTED_11_CONSTEXPR T value_or(U&& v) && {
4157
    static_assert(std::is_move_constructible<T>::value &&
4158
                      std::is_convertible<U&&, T>::value,
4159
                  "T must be move-constructible and convertible to from U&&");
4160
    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
4161
  }
4162
};
4163
4164
namespace detail {
4165
template <class Exp>
4166
using exp_t = typename detail::decay_t<Exp>::value_type;
4167
template <class Exp>
4168
using err_t = typename detail::decay_t<Exp>::error_type;
4169
template <class Exp, class Ret>
4170
using ret_t = expected<Ret, err_t<Exp>>;
4171
4172
#ifdef TL_EXPECTED_CXX14
4173
template <class Exp, class F,
4174
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4175
          class Ret = decltype(detail::invoke(std::declval<F>(),
4176
                                              *std::declval<Exp>()))>
4177
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4178
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4179
4180
  return exp.has_value()
4181
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4182
             : Ret(unexpect, std::forward<Exp>(exp).error());
4183
}
4184
4185
template <class Exp, class F,
4186
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4187
          class Ret = decltype(detail::invoke(std::declval<F>()))>
4188
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4189
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4190
4191
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4192
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4193
}
4194
#else
4195
template <class>
4196
struct TC;
4197
template <class Exp, class F,
4198
          class Ret = decltype(detail::invoke(std::declval<F>(),
4199
                                              *std::declval<Exp>())),
4200
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr>
4201
auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4202
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4203
4204
  return exp.has_value()
4205
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4206
             : Ret(unexpect, std::forward<Exp>(exp).error());
4207
}
4208
4209
template <class Exp, class F,
4210
          class Ret = decltype(detail::invoke(std::declval<F>())),
4211
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr>
4212
constexpr auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4213
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4214
4215
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4216
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4217
}
4218
#endif
4219
4220
#ifdef TL_EXPECTED_CXX14
4221
template <class Exp, class F,
4222
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4223
          class Ret = decltype(detail::invoke(std::declval<F>(),
4224
                                              *std::declval<Exp>())),
4225
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4226
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4227
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4228
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4229
                                                 *std::forward<Exp>(exp)))
4230
                         : result(unexpect, std::forward<Exp>(exp).error());
4231
}
4232
4233
template <class Exp, class F,
4234
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4235
          class Ret = decltype(detail::invoke(std::declval<F>(),
4236
                                              *std::declval<Exp>())),
4237
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4238
auto expected_map_impl(Exp&& exp, F&& f) {
4239
  using result = expected<void, err_t<Exp>>;
4240
  if (exp.has_value()) {
4241
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4242
    return result();
4243
  }
4244
4245
  return result(unexpect, std::forward<Exp>(exp).error());
4246
}
4247
4248
template <class Exp, class F,
4249
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4250
          class Ret = decltype(detail::invoke(std::declval<F>())),
4251
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4252
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4253
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4254
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4255
                         : result(unexpect, std::forward<Exp>(exp).error());
4256
}
4257
4258
template <class Exp, class F,
4259
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4260
          class Ret = decltype(detail::invoke(std::declval<F>())),
4261
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4262
auto expected_map_impl(Exp&& exp, F&& f) {
4263
  using result = expected<void, err_t<Exp>>;
4264
  if (exp.has_value()) {
4265
    detail::invoke(std::forward<F>(f));
4266
    return result();
4267
  }
4268
4269
  return result(unexpect, std::forward<Exp>(exp).error());
4270
}
4271
#else
4272
template <class Exp, class F,
4273
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4274
          class Ret = decltype(detail::invoke(std::declval<F>(),
4275
                                              *std::declval<Exp>())),
4276
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4277
4278
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4279
    -> ret_t<Exp, detail::decay_t<Ret>> {
4280
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4281
4282
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4283
                                                 *std::forward<Exp>(exp)))
4284
                         : result(unexpect, std::forward<Exp>(exp).error());
4285
}
4286
4287
template <class Exp, class F,
4288
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4289
          class Ret = decltype(detail::invoke(std::declval<F>(),
4290
                                              *std::declval<Exp>())),
4291
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4292
4293
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4294
  if (exp.has_value()) {
4295
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4296
    return {};
4297
  }
4298
4299
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4300
}
4301
4302
template <class Exp, class F,
4303
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4304
          class Ret = decltype(detail::invoke(std::declval<F>())),
4305
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4306
4307
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4308
    -> ret_t<Exp, detail::decay_t<Ret>> {
4309
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4310
4311
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4312
                         : result(unexpect, std::forward<Exp>(exp).error());
4313
}
4314
4315
template <class Exp, class F,
4316
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4317
          class Ret = decltype(detail::invoke(std::declval<F>())),
4318
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4319
4320
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4321
  if (exp.has_value()) {
4322
    detail::invoke(std::forward<F>(f));
4323
    return {};
4324
  }
4325
4326
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4327
}
4328
#endif
4329
4330
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4331
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4332
template <class Exp, class F,
4333
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4334
          class Ret = decltype(detail::invoke(std::declval<F>(),
4335
                                              std::declval<Exp>().error())),
4336
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4337
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4338
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4339
  return exp.has_value()
4340
             ? result(*std::forward<Exp>(exp))
4341
             : result(unexpect, detail::invoke(std::forward<F>(f),
4342
                                               std::forward<Exp>(exp).error()));
4343
}
4344
template <class Exp, class F,
4345
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4346
          class Ret = decltype(detail::invoke(std::declval<F>(),
4347
                                              std::declval<Exp>().error())),
4348
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4349
auto map_error_impl(Exp&& exp, F&& f) {
4350
  using result = expected<exp_t<Exp>, monostate>;
4351
  if (exp.has_value()) {
4352
    return result(*std::forward<Exp>(exp));
4353
  }
4354
4355
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4356
  return result(unexpect, monostate{});
4357
}
4358
template <class Exp, class F,
4359
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4360
          class Ret = decltype(detail::invoke(std::declval<F>(),
4361
                                              std::declval<Exp>().error())),
4362
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4363
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4364
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4365
  return exp.has_value()
4366
             ? result()
4367
             : result(unexpect, detail::invoke(std::forward<F>(f),
4368
                                               std::forward<Exp>(exp).error()));
4369
}
4370
template <class Exp, class F,
4371
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4372
          class Ret = decltype(detail::invoke(std::declval<F>(),
4373
                                              std::declval<Exp>().error())),
4374
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4375
auto map_error_impl(Exp&& exp, F&& f) {
4376
  using result = expected<exp_t<Exp>, monostate>;
4377
  if (exp.has_value()) {
4378
    return result();
4379
  }
4380
4381
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4382
  return result(unexpect, monostate{});
4383
}
4384
#else
4385
template <class Exp, class F,
4386
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4387
          class Ret = decltype(detail::invoke(std::declval<F>(),
4388
                                              std::declval<Exp>().error())),
4389
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4390
constexpr auto map_error_impl(Exp&& exp, F&& f)
4391
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4392
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4393
4394
  return exp.has_value()
4395
             ? result(*std::forward<Exp>(exp))
4396
             : result(unexpect, detail::invoke(std::forward<F>(f),
4397
                                               std::forward<Exp>(exp).error()));
4398
}
4399
4400
template <class Exp, class F,
4401
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4402
          class Ret = decltype(detail::invoke(std::declval<F>(),
4403
                                              std::declval<Exp>().error())),
4404
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4405
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4406
  using result = expected<exp_t<Exp>, monostate>;
4407
  if (exp.has_value()) {
4408
    return result(*std::forward<Exp>(exp));
4409
  }
4410
4411
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4412
  return result(unexpect, monostate{});
4413
}
4414
4415
template <class Exp, class F,
4416
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4417
          class Ret = decltype(detail::invoke(std::declval<F>(),
4418
                                              std::declval<Exp>().error())),
4419
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4420
constexpr auto map_error_impl(Exp&& exp, F&& f)
4421
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4422
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4423
4424
  return exp.has_value()
4425
             ? result()
4426
             : result(unexpect, detail::invoke(std::forward<F>(f),
4427
                                               std::forward<Exp>(exp).error()));
4428
}
4429
4430
template <class Exp, class F,
4431
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4432
          class Ret = decltype(detail::invoke(std::declval<F>(),
4433
                                              std::declval<Exp>().error())),
4434
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4435
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4436
  using result = expected<exp_t<Exp>, monostate>;
4437
  if (exp.has_value()) {
4438
    return result();
4439
  }
4440
4441
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4442
  return result(unexpect, monostate{});
4443
}
4444
#endif
4445
4446
#ifdef TL_EXPECTED_CXX14
4447
template <class Exp, class F,
4448
          class Ret = decltype(detail::invoke(std::declval<F>(),
4449
                                              std::declval<Exp>().error())),
4450
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4451
constexpr auto or_else_impl(Exp&& exp, F&& f) {
4452
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4453
  return exp.has_value() ? std::forward<Exp>(exp)
4454
                         : detail::invoke(std::forward<F>(f),
4455
                                          std::forward<Exp>(exp).error());
4456
}
4457
4458
template <class Exp, class F,
4459
          class Ret = decltype(detail::invoke(std::declval<F>(),
4460
                                              std::declval<Exp>().error())),
4461
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4462
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4463
  return exp.has_value() ? std::forward<Exp>(exp)
4464
                         : (detail::invoke(std::forward<F>(f),
4465
                                           std::forward<Exp>(exp).error()),
4466
                            std::forward<Exp>(exp));
4467
}
4468
#else
4469
template <class Exp, class F,
4470
          class Ret = decltype(detail::invoke(std::declval<F>(),
4471
                                              std::declval<Exp>().error())),
4472
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4473
auto or_else_impl(Exp&& exp, F&& f) -> Ret {
4474
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4475
  return exp.has_value() ? std::forward<Exp>(exp)
4476
                         : detail::invoke(std::forward<F>(f),
4477
                                          std::forward<Exp>(exp).error());
4478
}
4479
4480
template <class Exp, class F,
4481
          class Ret = decltype(detail::invoke(std::declval<F>(),
4482
                                              std::declval<Exp>().error())),
4483
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4484
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4485
  return exp.has_value() ? std::forward<Exp>(exp)
4486
                         : (detail::invoke(std::forward<F>(f),
4487
                                           std::forward<Exp>(exp).error()),
4488
                            std::forward<Exp>(exp));
4489
}
4490
#endif
4491
}  // namespace detail
4492
4493
template <class T, class E, class U, class F>
4494
constexpr bool operator==(const expected<T, E>& lhs,
4495
                          const expected<U, F>& rhs) {
4496
  return (lhs.has_value() != rhs.has_value())
4497
             ? false
4498
             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
4499
}
4500
template <class T, class E, class U, class F>
4501
constexpr bool operator!=(const expected<T, E>& lhs,
4502
                          const expected<U, F>& rhs) {
4503
  return (lhs.has_value() != rhs.has_value())
4504
             ? true
4505
             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4506
}
4507
template <class E, class F>
4508
constexpr bool operator==(const expected<void, E>& lhs,
4509
                          const expected<void, F>& rhs) {
4510
  return (lhs.has_value() != rhs.has_value())
4511
             ? false
4512
             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
4513
}
4514
template <class E, class F>
4515
constexpr bool operator!=(const expected<void, E>& lhs,
4516
                          const expected<void, F>& rhs) {
4517
  return (lhs.has_value() != rhs.has_value())
4518
             ? true
4519
             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4520
}
4521
4522
template <class T, class E, class U>
4523
constexpr bool operator==(const expected<T, E>& x, const U& v) {
4524
  return x.has_value() ? *x == v : false;
4525
}
4526
template <class T, class E, class U>
4527
constexpr bool operator==(const U& v, const expected<T, E>& x) {
4528
  return x.has_value() ? *x == v : false;
4529
}
4530
template <class T, class E, class U>
4531
constexpr bool operator!=(const expected<T, E>& x, const U& v) {
4532
  return x.has_value() ? *x != v : true;
4533
}
4534
template <class T, class E, class U>
4535
constexpr bool operator!=(const U& v, const expected<T, E>& x) {
4536
  return x.has_value() ? *x != v : true;
4537
}
4538
4539
template <class T, class E>
4540
constexpr bool operator==(const expected<T, E>& x, const unexpected<E>& e) {
4541
  return x.has_value() ? false : x.error() == e.value();
4542
}
4543
template <class T, class E>
4544
constexpr bool operator==(const unexpected<E>& e, const expected<T, E>& x) {
4545
  return x.has_value() ? false : x.error() == e.value();
4546
}
4547
template <class T, class E>
4548
constexpr bool operator!=(const expected<T, E>& x, const unexpected<E>& e) {
4549
  return x.has_value() ? true : x.error() != e.value();
4550
}
4551
template <class T, class E>
4552
constexpr bool operator!=(const unexpected<E>& e, const expected<T, E>& x) {
4553
  return x.has_value() ? true : x.error() != e.value();
4554
}
4555
4556
template <class T, class E,
4557
          detail::enable_if_t<(std::is_void<T>::value ||
4558
                               std::is_move_constructible<T>::value) &&
4559
                              detail::is_swappable<T>::value &&
4560
                              std::is_move_constructible<E>::value &&
4561
                              detail::is_swappable<E>::value>* = nullptr>
4562
void swap(expected<T, E>& lhs,
4563
          expected<T, E>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
4564
  lhs.swap(rhs);
4565
}
4566
}  // namespace tl
4567
4568
#endif
4569
/* end file include/ada/expected.h */
4570
4571
/* begin file include/ada/url_pattern_regex.h */
4572
/**
4573
 * @file url_search_params.h
4574
 * @brief Declaration for the URL Search Params
4575
 */
4576
#ifndef ADA_URL_PATTERN_REGEX_H
4577
#define ADA_URL_PATTERN_REGEX_H
4578
4579
#include <concepts>
4580
#include <optional>
4581
#include <string>
4582
#include <string_view>
4583
#include <vector>
4584
4585
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4586
#include <regex>
4587
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4588
4589
#if ADA_INCLUDE_URL_PATTERN
4590
namespace ada::url_pattern_regex {
4591
4592
/**
4593
 * @brief Concept defining the interface for regex engine providers used by
4594
 * URLPattern.
4595
 *
4596
 * A regex provider supplies the regular expression engine used for
4597
 * pattern compilation and matching.
4598
 *
4599
 * Requirements:
4600
 * - typename T::regex_type: The compiled regex object type.
4601
 * - T::create_instance(pattern, ignore_case): Compiles the regex pattern
4602
 * string. Returns std::nullopt if the pattern is invalid or cannot be compiled.
4603
 * - T::regex_search(input, pattern): Executes a regex search against the input,
4604
 *     returning a vector of capture group values (with std::nullopt for
4605
 * unmatched optional groups) or std::nullopt if the input does not match.
4606
 * - T::regex_match(input, pattern): Returns true if the input matches the
4607
 * pattern, or false otherwise.
4608
 *
4609
 * Exception contract:
4610
 * Provider methods may throw exceptions (for example, on allocation failure,
4611
 * input length limits, complexity limits, or internal engine errors). In
4612
 * environments where C++ exceptions are enabled, exceptions thrown by the
4613
 * provider will propagate out of URLPattern functions (such as fast_test,
4614
 * fast_match, test, exec, match, etc.). Providers that prefer non-throwing
4615
 * operation should handle internal errors and return false or std::nullopt.
4616
 */
4617
template <typename T>
4618
concept regex_concept = requires(T t, std::string_view pattern,
4619
                                 bool ignore_case, std::string_view input) {
4620
  // Ensure the class has a type alias 'regex_type'
4621
  typename T::regex_type;
4622
4623
  // Function to create a regex instance
4624
  {
4625
    T::create_instance(pattern, ignore_case)
4626
  } -> std::same_as<std::optional<typename T::regex_type>>;
4627
4628
  // Function to perform regex search
4629
  {
4630
    T::regex_search(input, std::declval<typename T::regex_type&>())
4631
  } -> std::same_as<std::optional<std::vector<std::optional<std::string>>>>;
4632
4633
  // Function to match regex pattern
4634
  {
4635
    T::regex_match(input, std::declval<typename T::regex_type&>())
4636
  } -> std::same_as<bool>;
4637
4638
  // Copy constructor
4639
  { T(std::declval<const T&>()) } -> std::same_as<T>;
4640
4641
  // Move constructor
4642
  { T(std::declval<T&&>()) } -> std::same_as<T>;
4643
};
4644
4645
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4646
class std_regex_provider final {
4647
 public:
4648
  std_regex_provider() = default;
4649
  using regex_type = std::regex;
4650
  static std::optional<regex_type> create_instance(std::string_view pattern,
4651
                                                   bool ignore_case);
4652
  static std::optional<std::vector<std::optional<std::string>>> regex_search(
4653
      std::string_view input, const regex_type& pattern);
4654
  static bool regex_match(std::string_view input, const regex_type& pattern);
4655
};
4656
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4657
4658
}  // namespace ada::url_pattern_regex
4659
#endif  // ADA_INCLUDE_URL_PATTERN
4660
#endif  // ADA_URL_PATTERN_REGEX_H
4661
/* end file include/ada/url_pattern_regex.h */
4662
/* begin file include/ada/url_pattern_init.h */
4663
/**
4664
 * @file url_pattern_init.h
4665
 * @brief Declaration for the url_pattern_init implementation.
4666
 */
4667
#ifndef ADA_URL_PATTERN_INIT_H
4668
#define ADA_URL_PATTERN_INIT_H
4669
4670
/* begin file include/ada/errors.h */
4671
/**
4672
 * @file errors.h
4673
 * @brief Error type definitions for URL parsing.
4674
 *
4675
 * Defines the error codes that can be returned when URL parsing fails.
4676
 */
4677
#ifndef ADA_ERRORS_H
4678
#define ADA_ERRORS_H
4679
4680
#include <cstdint>
4681
namespace ada {
4682
/**
4683
 * @brief Error codes for URL parsing operations.
4684
 *
4685
 * Used with `tl::expected` to indicate why a URL parsing operation failed.
4686
 */
4687
enum class errors : uint8_t {
4688
  type_error /**< A type error occurred (e.g., invalid URL syntax). */
4689
};
4690
}  // namespace ada
4691
#endif  // ADA_ERRORS_H
4692
/* end file include/ada/errors.h */
4693
4694
#include <concepts>
4695
#include <cstdint>
4696
#include <iostream>
4697
#include <optional>
4698
#include <string>
4699
#include <string_view>
4700
4701
#if ADA_TESTING
4702
#include <iostream>
4703
#endif  // ADA_TESTING
4704
4705
#if ADA_INCLUDE_URL_PATTERN
4706
namespace ada {
4707
4708
// Important: C++20 allows us to use concept rather than `using` or `typedef
4709
// and allows functions with second argument, which is optional (using either
4710
// std::nullopt or a parameter with default value)
4711
template <typename F>
4712
concept url_pattern_encoding_callback = requires(F f, std::string_view sv) {
4713
  { f(sv) } -> std::same_as<tl::expected<std::string, errors>>;
4714
};
4715
4716
// A structure providing matching patterns for individual components
4717
// of a URL. When a URLPattern is created, or when a URLPattern is
4718
// used to match or test against a URL, the input can be given as
4719
// either a string or a URLPatternInit struct. If a string is given,
4720
// it will be parsed to create a URLPatternInit. The URLPatternInit
4721
// API is defined as part of the URLPattern specification.
4722
// All provided strings must be valid UTF-8.
4723
struct url_pattern_init {
4724
  enum class process_type : uint8_t {
4725
    url,
4726
    pattern,
4727
  };
4728
4729
0
  friend std::ostream& operator<<(std::ostream& os, process_type type) {
4730
0
    switch (type) {
4731
0
      case process_type::url:
4732
0
        return os << "url";
4733
0
      case process_type::pattern:
4734
0
        return os << "pattern";
4735
0
      default:
4736
0
        return os << "unknown";
4737
0
    }
4738
0
  }
4739
4740
  // All strings must be valid UTF-8.
4741
  // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
4742
  static tl::expected<url_pattern_init, errors> process(
4743
      const url_pattern_init& init, process_type type,
4744
      std::optional<std::string_view> protocol = std::nullopt,
4745
      std::optional<std::string_view> username = std::nullopt,
4746
      std::optional<std::string_view> password = std::nullopt,
4747
      std::optional<std::string_view> hostname = std::nullopt,
4748
      std::optional<std::string_view> port = std::nullopt,
4749
      std::optional<std::string_view> pathname = std::nullopt,
4750
      std::optional<std::string_view> search = std::nullopt,
4751
      std::optional<std::string_view> hash = std::nullopt);
4752
4753
  // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init
4754
  static tl::expected<std::string, errors> process_protocol(
4755
      std::string_view value, process_type type);
4756
4757
  // @see https://urlpattern.spec.whatwg.org/#process-username-for-init
4758
  static tl::expected<std::string, errors> process_username(
4759
      std::string_view value, process_type type);
4760
4761
  // @see https://urlpattern.spec.whatwg.org/#process-password-for-init
4762
  static tl::expected<std::string, errors> process_password(
4763
      std::string_view value, process_type type);
4764
4765
  // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init
4766
  static tl::expected<std::string, errors> process_hostname(
4767
      std::string_view value, process_type type);
4768
4769
  // @see https://urlpattern.spec.whatwg.org/#process-port-for-init
4770
  static tl::expected<std::string, errors> process_port(
4771
      std::string_view port, std::string_view protocol, process_type type);
4772
4773
  // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init
4774
  static tl::expected<std::string, errors> process_pathname(
4775
      std::string_view value, std::string_view protocol, process_type type);
4776
4777
  // @see https://urlpattern.spec.whatwg.org/#process-search-for-init
4778
  static tl::expected<std::string, errors> process_search(
4779
      std::string_view value, process_type type);
4780
4781
  // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init
4782
  static tl::expected<std::string, errors> process_hash(std::string_view value,
4783
                                                        process_type type);
4784
4785
#if ADA_TESTING
4786
  friend void PrintTo(const url_pattern_init& init, std::ostream* os) {
4787
    *os << "protocol: '" << init.protocol.value_or("undefined") << "', ";
4788
    *os << "username: '" << init.username.value_or("undefined") << "', ";
4789
    *os << "password: '" << init.password.value_or("undefined") << "', ";
4790
    *os << "hostname: '" << init.hostname.value_or("undefined") << "', ";
4791
    *os << "port: '" << init.port.value_or("undefined") << "', ";
4792
    *os << "pathname: '" << init.pathname.value_or("undefined") << "', ";
4793
    *os << "search: '" << init.search.value_or("undefined") << "', ";
4794
    *os << "hash: '" << init.hash.value_or("undefined") << "', ";
4795
    *os << "base_url: '" << init.base_url.value_or("undefined") << "', ";
4796
  }
4797
#endif  // ADA_TESTING
4798
4799
  bool operator==(const url_pattern_init&) const;
4800
  // If present, must be valid UTF-8.
4801
  std::optional<std::string> protocol{};
4802
  // If present, must be valid UTF-8.
4803
  std::optional<std::string> username{};
4804
  // If present, must be valid UTF-8.
4805
  std::optional<std::string> password{};
4806
  // If present, must be valid UTF-8.
4807
  std::optional<std::string> hostname{};
4808
  // If present, must be valid UTF-8.
4809
  std::optional<std::string> port{};
4810
  // If present, must be valid UTF-8.
4811
  std::optional<std::string> pathname{};
4812
  // If present, must be valid UTF-8.
4813
  std::optional<std::string> search{};
4814
  // If present, must be valid UTF-8.
4815
  std::optional<std::string> hash{};
4816
  // If present, must be valid UTF-8.
4817
  std::optional<std::string> base_url{};
4818
};
4819
}  // namespace ada
4820
#endif  // ADA_INCLUDE_URL_PATTERN
4821
#endif  // ADA_URL_PATTERN_INIT_H
4822
/* end file include/ada/url_pattern_init.h */
4823
4824
/** @private Forward declarations */
4825
namespace ada {
4826
struct url_aggregator;
4827
struct url;
4828
#if ADA_INCLUDE_URL_PATTERN
4829
template <url_pattern_regex::regex_concept regex_provider>
4830
class url_pattern;
4831
struct url_pattern_options;
4832
#endif  // ADA_INCLUDE_URL_PATTERN
4833
enum class errors : uint8_t;
4834
}  // namespace ada
4835
4836
/**
4837
 * @namespace ada::parser
4838
 * @brief Internal URL parsing implementation.
4839
 *
4840
 * Contains the core URL parsing algorithm as specified by the WHATWG URL
4841
 * Standard. These functions are used internally by `ada::parse()`.
4842
 */
4843
namespace ada::parser {
4844
/**
4845
 * Parses a URL string into a URL object.
4846
 *
4847
 * @tparam result_type The type of URL object to create (url or url_aggregator).
4848
 *
4849
 * @param user_input The URL string to parse (must be valid UTF-8).
4850
 * @param base_url Optional base URL for resolving relative URLs.
4851
 *
4852
 * @return The parsed URL object. Check `is_valid` to determine if parsing
4853
 *         succeeded.
4854
 *
4855
 * @see https://url.spec.whatwg.org/#concept-basic-url-parser
4856
 */
4857
template <typename result_type = url_aggregator>
4858
result_type parse_url(std::string_view user_input,
4859
                      const result_type* base_url = nullptr);
4860
4861
extern template url_aggregator parse_url<url_aggregator>(
4862
    std::string_view user_input, const url_aggregator* base_url);
4863
extern template url parse_url<url>(std::string_view user_input,
4864
                                   const url* base_url);
4865
4866
template <typename result_type = url_aggregator, bool store_values = true>
4867
result_type parse_url_impl(std::string_view user_input,
4868
                           const result_type* base_url = nullptr);
4869
4870
extern template url_aggregator parse_url_impl<url_aggregator, true>(
4871
    std::string_view user_input, const url_aggregator* base_url);
4872
extern template url_aggregator parse_url_impl<url_aggregator, false>(
4873
    std::string_view user_input, const url_aggregator* base_url);
4874
extern template url parse_url_impl<url, true>(std::string_view user_input,
4875
                                              const url* base_url);
4876
4877
/** @private */
4878
template <class result_type>
4879
bool try_parse_simple_absolute(std::string_view input, result_type& out);
4880
4881
/** @private */
4882
template <class result_type>
4883
bool finish_simple_absolute_with_port(std::string_view input, result_type& out,
4884
                                      ada::scheme::type scheme_type,
4885
                                      uint32_t protocol_end, size_t host_start,
4886
                                      size_t host_end, size_t host_len,
4887
                                      bool has_upper);
4888
4889
/** @private */
4890
template <class result_type>
4891
bool try_parse_simple_relative(std::string_view input, const result_type& base,
4892
                               result_type& out);
4893
4894
#if ADA_INCLUDE_URL_PATTERN
4895
template <url_pattern_regex::regex_concept regex_provider>
4896
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
4897
    std::variant<std::string_view, url_pattern_init>&& input,
4898
    const std::string_view* base_url, const url_pattern_options* options);
4899
#endif  // ADA_INCLUDE_URL_PATTERN
4900
4901
}  // namespace ada::parser
4902
4903
#endif  // ADA_PARSER_H
4904
/* end file include/ada/parser.h */
4905
/* begin file include/ada/parser-inl.h */
4906
/**
4907
 * @file parser-inl.h
4908
 */
4909
#ifndef ADA_PARSER_INL_H
4910
#define ADA_PARSER_INL_H
4911
4912
/* begin file include/ada/url_pattern.h */
4913
/**
4914
 * @file url_pattern.h
4915
 * @brief URLPattern API implementation.
4916
 *
4917
 * This header provides the URLPattern API as specified by the WHATWG URL
4918
 * Pattern Standard. URLPattern allows matching URLs against patterns with
4919
 * wildcards and named groups, similar to how regular expressions match strings.
4920
 *
4921
 * @see https://urlpattern.spec.whatwg.org/
4922
 * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
4923
 */
4924
#ifndef ADA_URL_PATTERN_H
4925
#define ADA_URL_PATTERN_H
4926
4927
/* begin file include/ada/implementation.h */
4928
/**
4929
 * @file implementation.h
4930
 * @brief User-facing functions for URL parsing and manipulation.
4931
 *
4932
 * This header provides the primary public API for parsing URLs in Ada.
4933
 * It includes the main `ada::parse()` function which is the recommended
4934
 * entry point for most users.
4935
 *
4936
 * @see https://url.spec.whatwg.org/#api
4937
 */
4938
#ifndef ADA_IMPLEMENTATION_H
4939
#define ADA_IMPLEMENTATION_H
4940
4941
#include <cstdint>
4942
#include <optional>
4943
#include <span>
4944
#include <string>
4945
#include <string_view>
4946
#include <variant>
4947
4948
/* begin file include/ada/url.h */
4949
/**
4950
 * @file url.h
4951
 * @brief Declaration for the `ada::url` class.
4952
 *
4953
 * This file contains the `ada::url` struct which represents a parsed URL
4954
 * using separate `std::string` instances for each component. This
4955
 * representation is more flexible but uses more memory than `url_aggregator`.
4956
 *
4957
 * @see url_aggregator.h for a more memory-efficient alternative
4958
 */
4959
#ifndef ADA_URL_H
4960
#define ADA_URL_H
4961
4962
#include <algorithm>
4963
#include <cstddef>
4964
#include <cstdint>
4965
#include <optional>
4966
#include <ostream>
4967
#include <string>
4968
#include <string_view>
4969
4970
/* begin file include/ada/url_components.h */
4971
/**
4972
 * @file url_components.h
4973
 * @brief URL component offset representation for url_aggregator.
4974
 *
4975
 * This file defines the `url_components` struct which stores byte offsets
4976
 * into a URL string buffer. It is used internally by `url_aggregator` to
4977
 * efficiently locate URL components without storing separate strings.
4978
 */
4979
#ifndef ADA_URL_COMPONENTS_H
4980
#define ADA_URL_COMPONENTS_H
4981
4982
#include <cstdint>
4983
#include <string>
4984
4985
namespace ada {
4986
4987
/**
4988
 * @brief Stores byte offsets for URL components within a buffer.
4989
 *
4990
 * The `url_components` struct uses 32-bit offsets to track the boundaries
4991
 * of each URL component within a single string buffer. This enables efficient
4992
 * component extraction without additional memory allocations.
4993
 *
4994
 * Component layout in a URL:
4995
 * ```
4996
 * https://user:pass@example.com:1234/foo/bar?baz#quux
4997
 *       |     |    |          | ^^^^|       |   |
4998
 *       |     |    |          | |   |       |   `----- hash_start
4999
 *       |     |    |          | |   |       `--------- search_start
5000
 *       |     |    |          | |   `----------------- pathname_start
5001
 *       |     |    |          | `--------------------- port
5002
 *       |     |    |          `----------------------- host_end
5003
 *       |     |    `---------------------------------- host_start
5004
 *       |     `--------------------------------------- username_end
5005
 *       `--------------------------------------------- protocol_end
5006
 * ```
5007
 *
5008
 * @note The 32-bit offsets limit URLs to 4GB in length.
5009
 * @note A value of `omitted` (UINT32_MAX) indicates the component is not
5010
 * present.
5011
 */
5012
struct url_components {
5013
  /** Sentinel value indicating a component is not present. */
5014
  constexpr static uint32_t omitted = uint32_t(-1);
5015
5016
13.4k
  url_components() = default;
5017
  url_components(const url_components& u) = default;
5018
  url_components(url_components&& u) noexcept = default;
5019
  url_components& operator=(url_components&& u) noexcept = default;
5020
  url_components& operator=(const url_components& u) = default;
5021
  ~url_components() = default;
5022
5023
  /** Offset of the end of the protocol/scheme (position of ':'). */
5024
  uint32_t protocol_end{0};
5025
5026
  /**
5027
   * Offset of the end of the username.
5028
   * Initialized to 0 (not `omitted`) to simplify username/password getters.
5029
   */
5030
  uint32_t username_end{0};
5031
5032
  /** Offset of the start of the host. */
5033
  uint32_t host_start{0};
5034
5035
  /** Offset of the end of the host. */
5036
  uint32_t host_end{0};
5037
5038
  /** Port number, or `omitted` if no port is specified. */
5039
  uint32_t port{omitted};
5040
5041
  /** Offset of the start of the pathname. */
5042
  uint32_t pathname_start{0};
5043
5044
  /** Offset of the '?' starting the query, or `omitted` if no query. */
5045
  uint32_t search_start{omitted};
5046
5047
  /** Offset of the '#' starting the fragment, or `omitted` if no fragment. */
5048
  uint32_t hash_start{omitted};
5049
5050
  /**
5051
   * Validates that offsets are in ascending order and consistent.
5052
   * Useful for debugging to detect internal corruption.
5053
   * @return `true` if offsets are consistent, `false` otherwise.
5054
   */
5055
  [[nodiscard]] constexpr bool check_offset_consistency() const noexcept;
5056
5057
  /**
5058
   * Returns a JSON string representation of the offsets for debugging.
5059
   * @return A JSON-formatted string with all offset values.
5060
   */
5061
  [[nodiscard]] std::string to_string() const;
5062
5063
};  // struct url_components
5064
}  // namespace ada
5065
#endif
5066
/* end file include/ada/url_components.h */
5067
5068
namespace ada {
5069
5070
struct url_aggregator;
5071
5072
// namespace parser {
5073
// template <typename result_type>
5074
// result_type parse_url(std::string_view user_input,
5075
//                       const result_type* base_url = nullptr);
5076
// template <typename result_type, bool store_values>
5077
// result_type parse_url_impl(std::string_view user_input,
5078
//                            const result_type* base_url = nullptr);
5079
// }
5080
5081
/**
5082
 * @brief Represents a parsed URL with individual string components.
5083
 *
5084
 * The `url` struct stores each URL component (scheme, username, password,
5085
 * host, port, path, query, fragment) as a separate `std::string`. This
5086
 * provides flexibility but incurs more memory allocations compared to
5087
 * `url_aggregator`.
5088
 *
5089
 * **When to use `ada::url`:**
5090
 * - When you need to frequently modify individual URL components
5091
 * - When you want independent ownership of component strings
5092
 *
5093
 * **When to use `ada::url_aggregator` instead:**
5094
 * - For read-mostly operations on parsed URLs
5095
 * - When memory efficiency is important
5096
 * - When you only need string_view access to components
5097
 *
5098
 * @note This type is returned when parsing with `ada::parse<ada::url>()`.
5099
 *       By default, `ada::parse()` returns `ada::url_aggregator`.
5100
 *
5101
 * @see url_aggregator For a more memory-efficient URL representation
5102
 * @see https://url.spec.whatwg.org/#url-representation
5103
 */
5104
struct url : url_base {
5105
0
  url() = default;
5106
0
  url(const url& u) = default;
5107
0
  url(url&& u) noexcept = default;
5108
0
  url& operator=(url&& u) noexcept = default;
5109
0
  url& operator=(const url& u) = default;
5110
0
  ~url() override = default;
5111
5112
  // Fields are ordered so that the most frequently accessed components
5113
  // tend to occupy earlier cache lines and remain close together in memory.
5114
  //
5115
  // Note: The exact object layout (including cache-line boundaries, byte
5116
  // offsets, and member sizes) is implementation- and platform-dependent.
5117
  // This ordering expresses an intent for better cache locality but does not
5118
  // guarantee any specific in-memory layout.
5119
5120
  /**
5121
   * @private
5122
   * A URL's host is null or a host. It is initially null.
5123
   */
5124
  std::optional<std::string> host{};
5125
5126
  /**
5127
   * @private
5128
   * A URL's path is either an ASCII string or a list of zero or more ASCII
5129
   * strings, usually identifying a location.
5130
   */
5131
  std::string path{};
5132
5133
  /**
5134
   * @private
5135
   * A URL's query is either null or an ASCII string. It is initially null.
5136
   */
5137
  std::optional<std::string> query{};
5138
5139
  /**
5140
   * @private
5141
   * A URL's fragment is either null or an ASCII string that can be used for
5142
   * further processing on the resource the URL's other components identify. It
5143
   * is initially null.
5144
   */
5145
  std::optional<std::string> hash{};
5146
5147
  /**
5148
   * @private
5149
   * A URL's port is either null or a 16-bit unsigned integer that identifies a
5150
   * networking port. It is initially null.
5151
   */
5152
  std::optional<uint16_t> port{};
5153
5154
  /**
5155
   * @private
5156
   * A URL's username is an ASCII string identifying a username. It is initially
5157
   * the empty string.
5158
   */
5159
  std::string username{};
5160
5161
  /**
5162
   * @private
5163
   * A URL's password is an ASCII string identifying a password. It is initially
5164
   * the empty string.
5165
   */
5166
  std::string password{};
5167
5168
  /**
5169
   * Checks if the URL has an empty hostname (host is set but empty string).
5170
   * @return `true` if host exists but is empty, `false` otherwise.
5171
   */
5172
  [[nodiscard]] inline bool has_empty_hostname() const noexcept;
5173
5174
  /**
5175
   * Checks if the URL has a non-default port explicitly specified.
5176
   * @return `true` if a port is present, `false` otherwise.
5177
   */
5178
  [[nodiscard]] inline bool has_port() const noexcept;
5179
5180
  /**
5181
   * Checks if the URL has a hostname (including empty hostnames).
5182
   * @return `true` if host is present, `false` otherwise.
5183
   */
5184
  [[nodiscard]] inline bool has_hostname() const noexcept;
5185
5186
  /**
5187
   * Validates whether the hostname is a valid domain according to RFC 1034.
5188
   * Checks that the domain and its labels have valid lengths (max 255 octets
5189
   * total, max 63 octets per label).
5190
   * @return `true` if the domain is valid, `false` otherwise.
5191
   */
5192
  [[nodiscard]] bool has_valid_domain() const noexcept override;
5193
5194
  /**
5195
   * Returns a JSON string representation of this URL for debugging.
5196
   * @return A JSON-formatted string with all URL components.
5197
   */
5198
  [[nodiscard]] std::string to_string() const override;
5199
5200
  /**
5201
   * Returns the full serialized URL (the href).
5202
   * @return The complete URL string (allocates a new string).
5203
   * @see https://url.spec.whatwg.org/#dom-url-href
5204
   */
5205
  [[nodiscard]] ada_really_inline std::string get_href() const;
5206
5207
  /**
5208
   * Returns the byte length of the serialized URL without allocating a string.
5209
   * @return Size of the href in bytes.
5210
   */
5211
  [[nodiscard]] size_t get_href_size() const noexcept;
5212
5213
  /**
5214
   * Returns the URL's origin as a string (scheme + host + port for special
5215
   * URLs).
5216
   * @return A newly allocated string containing the serialized origin.
5217
   * @see https://url.spec.whatwg.org/#concept-url-origin
5218
   */
5219
  [[nodiscard]] std::string get_origin() const override;
5220
5221
  /**
5222
   * Returns the URL's scheme followed by a colon (e.g., "https:").
5223
   * @return A newly allocated string with the protocol.
5224
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5225
   */
5226
  [[nodiscard]] std::string get_protocol() const;
5227
5228
  /**
5229
   * Returns the URL's host and port (e.g., "example.com:8080").
5230
   * If no port is set, returns just the host. Returns empty string if no host.
5231
   * @return A newly allocated string with host:port.
5232
   * @see https://url.spec.whatwg.org/#dom-url-host
5233
   */
5234
  [[nodiscard]] std::string get_host() const;
5235
5236
  /**
5237
   * Returns the URL's hostname (without port).
5238
   * Returns empty string if no host is set.
5239
   * @return A newly allocated string with the hostname.
5240
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5241
   */
5242
  [[nodiscard]] std::string get_hostname() const;
5243
5244
  /**
5245
   * Returns the URL's path component.
5246
   * @return A string_view pointing to the path.
5247
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5248
   */
5249
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept;
5250
5251
  /**
5252
   * Returns the byte length of the pathname without creating a string.
5253
   * @return Size of the pathname in bytes.
5254
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5255
   */
5256
  [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
5257
5258
  /**
5259
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
5260
   * Returns empty string if no query is set.
5261
   * @return A newly allocated string with the search/query.
5262
   * @see https://url.spec.whatwg.org/#dom-url-search
5263
   */
5264
  [[nodiscard]] std::string get_search() const;
5265
5266
  /**
5267
   * Returns the URL's username component.
5268
   * @return A constant reference to the username string.
5269
   * @see https://url.spec.whatwg.org/#dom-url-username
5270
   */
5271
  [[nodiscard]] const std::string& get_username() const noexcept;
5272
5273
  /**
5274
   * Sets the URL's username, percent-encoding special characters.
5275
   * @param input The new username value.
5276
   * @return `true` on success, `false` if the URL cannot have credentials.
5277
   * @see https://url.spec.whatwg.org/#dom-url-username
5278
   */
5279
  bool set_username(std::string_view input);
5280
5281
  /**
5282
   * Sets the URL's password, percent-encoding special characters.
5283
   * @param input The new password value.
5284
   * @return `true` on success, `false` if the URL cannot have credentials.
5285
   * @see https://url.spec.whatwg.org/#dom-url-password
5286
   */
5287
  bool set_password(std::string_view input);
5288
5289
  /**
5290
   * Sets the URL's port from a string (e.g., "8080").
5291
   * @param input The port string. Empty string removes the port.
5292
   * @return `true` on success, `false` if the URL cannot have a port.
5293
   * @see https://url.spec.whatwg.org/#dom-url-port
5294
   */
5295
  bool set_port(std::string_view input);
5296
5297
  /**
5298
   * Sets the URL's fragment/hash (the part after '#').
5299
   * @param input The new hash value (with or without leading '#').
5300
   * @see https://url.spec.whatwg.org/#dom-url-hash
5301
   */
5302
  void set_hash(std::string_view input);
5303
5304
  /**
5305
   * Sets the URL's query string (the part after '?').
5306
   * @param input The new query value (with or without leading '?').
5307
   * @see https://url.spec.whatwg.org/#dom-url-search
5308
   */
5309
  void set_search(std::string_view input);
5310
5311
  /**
5312
   * Sets the URL's pathname.
5313
   * @param input The new path value.
5314
   * @return `true` on success, `false` if the URL has an opaque path.
5315
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5316
   */
5317
  bool set_pathname(std::string_view input);
5318
5319
  /**
5320
   * Sets the URL's host (hostname and optionally port).
5321
   * @param input The new host value (e.g., "example.com:8080").
5322
   * @return `true` on success, `false` if parsing fails.
5323
   * @see https://url.spec.whatwg.org/#dom-url-host
5324
   */
5325
  bool set_host(std::string_view input);
5326
5327
  /**
5328
   * Sets the URL's hostname (without port).
5329
   * @param input The new hostname value.
5330
   * @return `true` on success, `false` if parsing fails.
5331
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5332
   */
5333
  bool set_hostname(std::string_view input);
5334
5335
  /**
5336
   * Sets the URL's protocol/scheme.
5337
   * @param input The new protocol (with or without trailing ':').
5338
   * @return `true` on success, `false` if the scheme is invalid.
5339
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5340
   */
5341
  bool set_protocol(std::string_view input);
5342
5343
  /**
5344
   * Replaces the entire URL by parsing a new href string.
5345
   * @param input The new URL string to parse.
5346
   * @return `true` on success, `false` if parsing fails.
5347
   * @see https://url.spec.whatwg.org/#dom-url-href
5348
   */
5349
  bool set_href(std::string_view input);
5350
5351
  /**
5352
   * Returns the URL's password component.
5353
   * @return A constant reference to the password string.
5354
   * @see https://url.spec.whatwg.org/#dom-url-password
5355
   */
5356
  [[nodiscard]] const std::string& get_password() const noexcept;
5357
5358
  /**
5359
   * Returns the URL's port as a string (e.g., "8080").
5360
   * Returns empty string if no port is set.
5361
   * @return A newly allocated string with the port.
5362
   * @see https://url.spec.whatwg.org/#dom-url-port
5363
   */
5364
  [[nodiscard]] std::string get_port() const;
5365
5366
  /**
5367
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
5368
   * Returns empty string if no fragment is set.
5369
   * @return A newly allocated string with the hash.
5370
   * @see https://url.spec.whatwg.org/#dom-url-hash
5371
   */
5372
  [[nodiscard]] std::string get_hash() const;
5373
5374
  /**
5375
   * Checks if the URL has credentials (non-empty username or password).
5376
   * @return `true` if username or password is non-empty, `false` otherwise.
5377
   */
5378
  [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5379
5380
  /**
5381
   * Returns the URL component offsets for efficient serialization.
5382
   *
5383
   * The components represent byte offsets into the serialized URL:
5384
   * ```
5385
   * https://user:pass@example.com:1234/foo/bar?baz#quux
5386
   *       |     |    |          | ^^^^|       |   |
5387
   *       |     |    |          | |   |       |   `----- hash_start
5388
   *       |     |    |          | |   |       `--------- search_start
5389
   *       |     |    |          | |   `----------------- pathname_start
5390
   *       |     |    |          | `--------------------- port
5391
   *       |     |    |          `----------------------- host_end
5392
   *       |     |    `---------------------------------- host_start
5393
   *       |     `--------------------------------------- username_end
5394
   *       `--------------------------------------------- protocol_end
5395
   * ```
5396
   * @return A newly constructed url_components struct.
5397
   * @see https://github.com/servo/rust-url
5398
   */
5399
  [[nodiscard]] ada_really_inline ada::url_components get_components() const;
5400
5401
  /**
5402
   * Checks if the URL has a fragment/hash component.
5403
   * @return `true` if hash is present, `false` otherwise.
5404
   */
5405
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
5406
5407
  /**
5408
   * Checks if the URL has a query/search component.
5409
   * @return `true` if query is present, `false` otherwise.
5410
   */
5411
  [[nodiscard]] constexpr bool has_search() const noexcept override;
5412
5413
 private:
5414
  friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5415
                                                   const ada::url*);
5416
  friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5417
      std::string_view, const ada::url_aggregator*);
5418
  friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5419
      ada::url& url);
5420
5421
  friend ada::url ada::parser::parse_url_impl<ada::url, true>(std::string_view,
5422
                                                              const ada::url*);
5423
  friend ada::url_aggregator ada::parser::parse_url_impl<
5424
      ada::url_aggregator, true>(std::string_view, const ada::url_aggregator*);
5425
  friend ada::url_aggregator ada::parser::parse_url_impl<
5426
      ada::url_aggregator, false>(std::string_view, const ada::url_aggregator*);
5427
  template <class result_type>
5428
  friend bool ada::parser::try_parse_simple_absolute(std::string_view,
5429
                                                     result_type&);
5430
  template <class result_type>
5431
  friend bool ada::parser::finish_simple_absolute_with_port(
5432
      std::string_view, result_type&, ada::scheme::type, uint32_t, size_t,
5433
      size_t, size_t, bool);
5434
  template <class result_type>
5435
  friend bool ada::parser::try_parse_simple_relative(std::string_view,
5436
                                                     const result_type&,
5437
                                                     result_type&);
5438
5439
  inline void update_unencoded_base_hash(std::string_view input);
5440
  inline void update_base_hostname(std::string_view input);
5441
  inline void update_base_search(std::string_view input,
5442
                                 const uint8_t query_percent_encode_set[]);
5443
  inline void update_base_search(std::optional<std::string>&& input);
5444
  inline void update_base_pathname(std::string_view input);
5445
  inline void update_base_username(std::string_view input);
5446
  inline void update_base_password(std::string_view input);
5447
  inline void update_base_port(std::optional<uint16_t> input);
5448
5449
  /**
5450
   * Returns true if growing href by input_len (worst case) could exceed
5451
   * get_max_input_length(), meaning a rollback snapshot is actually needed.
5452
   */
5453
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
5454
5455
  /**
5456
   * Sets the host or hostname according to override condition.
5457
   * Return true on success.
5458
   * @see https://url.spec.whatwg.org/#hostname-state
5459
   */
5460
  template <bool override_hostname = false>
5461
  bool set_host_or_hostname(std::string_view input);
5462
5463
  /**
5464
   * Return true on success.
5465
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5466
   */
5467
  [[nodiscard]] bool parse_ipv4(std::string_view input);
5468
5469
  /**
5470
   * Return true on success.
5471
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5472
   */
5473
  [[nodiscard]] bool parse_ipv6(std::string_view input);
5474
5475
  /**
5476
   * Return true on success.
5477
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5478
   */
5479
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
5480
5481
  /**
5482
   * A URL's scheme is an ASCII string that identifies the type of URL and can
5483
   * be used to dispatch a URL for further processing after parsing. It is
5484
   * initially the empty string. We only set non_special_scheme when the scheme
5485
   * is non-special, otherwise we avoid constructing string.
5486
   *
5487
   * Special schemes are stored in ada::scheme::details::is_special_list so we
5488
   * typically do not need to store them in each url instance.
5489
   */
5490
  std::string non_special_scheme{};
5491
5492
  /**
5493
   * A URL cannot have a username/password/port if its host is null or the empty
5494
   * string, or its scheme is "file".
5495
   */
5496
  [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5497
5498
  ada_really_inline size_t parse_port(
5499
      std::string_view view, bool check_trailing_content) noexcept override;
5500
5501
0
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5502
0
    return this->parse_port(view, false);
5503
0
  }
5504
5505
  /**
5506
   * Parse the host from the provided input. We assume that
5507
   * the input does not contain spaces or tabs. Control
5508
   * characters and spaces are not trimmed (they should have
5509
   * been removed if needed).
5510
   * Return true on success.
5511
   * @see https://url.spec.whatwg.org/#host-parsing
5512
   */
5513
  [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5514
5515
  template <bool has_state_override = false>
5516
  [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5517
5518
  constexpr void clear_pathname() override;
5519
  constexpr void clear_search() override;
5520
  constexpr void set_protocol_as_file();
5521
5522
  /**
5523
   * Parse the path from the provided input.
5524
   * Return true on success. Control characters not
5525
   * trimmed from the ends (they should have
5526
   * been removed if needed).
5527
   *
5528
   * The input is expected to be UTF-8.
5529
   *
5530
   * @see https://url.spec.whatwg.org/
5531
   */
5532
  ada_really_inline void parse_path(std::string_view input);
5533
5534
  /**
5535
   * Set the scheme for this URL. The provided scheme should be a valid
5536
   * scheme string, be lower-cased, not contain spaces or tabs. It should
5537
   * have no spurious trailing or leading content.
5538
   */
5539
  inline void set_scheme(std::string&& new_scheme) noexcept;
5540
5541
  /**
5542
   * Take the scheme from another URL. The scheme string is moved from the
5543
   * provided url.
5544
   */
5545
  constexpr void copy_scheme(ada::url&& u);
5546
5547
  /**
5548
   * Take the scheme from another URL. The scheme string is copied from the
5549
   * provided url.
5550
   */
5551
  constexpr void copy_scheme(const ada::url& u);
5552
5553
};  // struct url
5554
5555
inline std::ostream& operator<<(std::ostream& out, const ada::url& u);
5556
}  // namespace ada
5557
5558
#endif  // ADA_URL_H
5559
/* end file include/ada/url.h */
5560
5561
namespace ada {
5562
5563
/**
5564
 * Result type for URL parsing operations.
5565
 *
5566
 * Uses `tl::expected` to represent either a successfully parsed URL or an
5567
 * error. This allows for exception-free error handling.
5568
 *
5569
 * @tparam result_type The URL type to return (default: `ada::url_aggregator`)
5570
 *
5571
 * @example
5572
 * ```cpp
5573
 * ada::result<ada::url_aggregator> result = ada::parse("https://example.com");
5574
 * if (result) {
5575
 *     // Success: use result.value() or *result
5576
 * } else {
5577
 *     // Error: handle result.error()
5578
 * }
5579
 * ```
5580
 */
5581
template <class result_type = ada::url_aggregator>
5582
using result = tl::expected<result_type, ada::errors>;
5583
5584
/**
5585
 * Parses a URL string according to the WHATWG URL Standard.
5586
 *
5587
 * This is the main entry point for URL parsing in Ada. The function takes
5588
 * a string input and optionally a base URL for resolving relative URLs.
5589
 *
5590
 * @tparam result_type The URL type to return. Can be either `ada::url` or
5591
 *         `ada::url_aggregator` (default). The `url_aggregator` type is more
5592
 *         memory-efficient as it stores components as offsets into a single
5593
 *         buffer.
5594
 *
5595
 * @param input The URL string to parse. Must be valid ASCII or UTF-8 encoded.
5596
 *        Leading and trailing whitespace is automatically trimmed.
5597
 * @param base_url Optional pointer to a base URL for resolving relative URLs.
5598
 *        If nullptr (default), only absolute URLs can be parsed successfully.
5599
 *
5600
 * @return A `result<result_type>` containing either the parsed URL on success,
5601
 *         or an error code on failure. Use the boolean conversion or
5602
 *         `has_value()` to check for success.
5603
 *
5604
 * @note The parser is fully compliant with the WHATWG URL Standard.
5605
 *
5606
 * Parsing fails if the input or the resulting normalized URL exceeds
5607
 * `get_max_input_length()` bytes (default ~4 GB, configurable via
5608
 * `set_max_input_length()`). This accounts for percent-encoding expansion:
5609
 * a short input that normalizes into a long URL is still rejected.
5610
 *
5611
 * @example
5612
 * ```cpp
5613
 * // Parse an absolute URL
5614
 * auto url = ada::parse("https://user:pass@example.com:8080/path?query#hash");
5615
 * if (url) {
5616
 *     std::cout << url->get_hostname(); // "example.com"
5617
 *     std::cout << url->get_pathname(); // "/path"
5618
 * }
5619
 *
5620
 * // Parse a relative URL with a base
5621
 * auto base = ada::parse("https://example.com/dir/");
5622
 * if (base) {
5623
 *     auto relative = ada::parse("../other/page", &*base);
5624
 *     if (relative) {
5625
 *         std::cout << relative->get_href(); //
5626
 * "https://example.com/other/page"
5627
 *     }
5628
 * }
5629
 * ```
5630
 *
5631
 * @see https://url.spec.whatwg.org/#url-parsing
5632
 */
5633
template <class result_type = ada::url_aggregator>
5634
ada_warn_unused ada::result<result_type> parse(
5635
    std::string_view input, const result_type* base_url = nullptr);
5636
5637
extern template ada::result<url> parse<url>(std::string_view input,
5638
                                            const url* base_url);
5639
extern template ada::result<url_aggregator> parse<url_aggregator>(
5640
    std::string_view input, const url_aggregator* base_url);
5641
5642
/**
5643
 * Checks whether a URL string can be successfully parsed.
5644
 *
5645
 * Equivalent to `parse(input, base).has_value()` for every input, including
5646
 * when `set_max_input_length` rejects a normalized href that exceeds the limit.
5647
 *
5648
 * @param input The URL string to validate. Must be valid ASCII or UTF-8.
5649
 * @param base_input Optional base URL string for relative inputs.
5650
 * @return `true` if parsing would succeed, `false` otherwise.
5651
 * @see https://url.spec.whatwg.org/#dom-url-canparse
5652
 */
5653
bool can_parse(std::string_view input,
5654
               const std::string_view* base_input = nullptr);
5655
5656
#if ADA_INCLUDE_URL_PATTERN
5657
/**
5658
 * Parses a URL pattern according to the URLPattern specification.
5659
 *
5660
 * URL patterns provide a syntax for matching URLs against patterns, similar
5661
 * to how regular expressions match strings. This is useful for routing and
5662
 * URL-based dispatching.
5663
 *
5664
 * @tparam regex_provider The regex implementation to use for pattern matching.
5665
 *
5666
 * @param input Either a URL pattern string (valid UTF-8) or a URLPatternInit
5667
 *        struct specifying individual component patterns.
5668
 * @param base_url Optional pointer to a base URL string (valid UTF-8) for
5669
 *        resolving relative patterns.
5670
 * @param options Optional pointer to configuration options (e.g., ignore_case).
5671
 *
5672
 * @return A `tl::expected` containing either the parsed url_pattern on success,
5673
 *         or an error code on failure.
5674
 *
5675
 * @see https://urlpattern.spec.whatwg.org
5676
 */
5677
template <url_pattern_regex::regex_concept regex_provider>
5678
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
5679
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
5680
                  const std::string_view* base_url = nullptr,
5681
                  const url_pattern_options* options = nullptr);
5682
5683
template <url_pattern_regex::regex_concept regex_provider>
5684
class url_pattern_list;
5685
5686
/**
5687
 * Compiles a set of URLPattern pathname patterns into an ada::url_pattern_list
5688
 * (experimental): one structure that answers "which route matches this
5689
 * pathname, and what are its group values?" without a loop over the
5690
 * patterns and without executing regular expressions for routes written in
5691
 * the static / ":param" / "*" subset.
5692
 *
5693
 * @tparam regex_provider The regex implementation, as for parse_url_pattern;
5694
 *         it is used only for routes that need URLPattern regexp semantics.
5695
 *
5696
 * @param pathname_patterns URLPattern pathname patterns (valid UTF-8), for
5697
 *        example "/users/:id". The index of a pattern is the route index
5698
 *        reported by url_pattern_list::match; duplicates are allowed.
5699
 * @param base_url Optional pointer to a base URL string (valid UTF-8): each
5700
 *        pattern is then processed as the pathname of a URLPatternInit with
5701
 *        that base URL (relative patterns resolve against its path).
5702
 * @param options Optional pointer to configuration options (ignore_case),
5703
 *        applied to every route as parse_url_pattern applies them.
5704
 *
5705
 * @return A `tl::expected` containing either the compiled list on success,
5706
 *         or an error code when a pattern is not a valid URLPattern pathname
5707
 *         pattern (errors::type_error, as the URLPattern constructor).
5708
 *
5709
 * @see https://github.com/whatwg/urlpattern/issues/166
5710
 */
5711
template <url_pattern_regex::regex_concept regex_provider>
5712
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
5713
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
5714
                       const std::string_view* base_url = nullptr,
5715
                       const url_pattern_options* options = nullptr);
5716
5717
/**
5718
 * Compiles the pathname components of existing url_pattern objects into an
5719
 * ada::url_pattern_list (experimental). Only the pathname component of each
5720
 * pattern is used; routes that need the regex provider reuse the pattern's
5721
 * already compiled pathname component. All patterns must share the same
5722
 * ignore_case setting (errors::type_error otherwise); the pathname is
5723
 * compiled as for a special-scheme URL.
5724
 */
5725
template <url_pattern_regex::regex_concept regex_provider>
5726
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
5727
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns);
5728
#endif  // ADA_INCLUDE_URL_PATTERN
5729
5730
/**
5731
 * Converts a file system path to a file:// URL.
5732
 *
5733
 * Creates a properly formatted file URL from a local file system path.
5734
 * Handles platform-specific path separators and percent-encoding.
5735
 *
5736
 * Respects `get_max_input_length()`: if the input path or the resulting
5737
 * `file://` href would exceed the limit, returns an empty string.
5738
 *
5739
 * @param path The file system path to convert. Must be valid ASCII or UTF-8.
5740
 *
5741
 * @return A file:// URL string representing the given path, or empty on
5742
 *         length-limit rejection.
5743
 */
5744
std::string href_from_file(std::string_view path);
5745
5746
/**
5747
 * Sets the maximum allowed length for URLs.
5748
 *
5749
 * Both the raw input and the resulting normalized URL (the href) are checked
5750
 * against this limit. Parsing or setter calls that would produce a URL
5751
 * exceeding this length are rejected. The same limit also applies to
5752
 * `href_from_file` and to query strings passed to `url_search_params`
5753
 * construction / `reset`. The value must fit in a uint32_t.
5754
 * The default is std::numeric_limits<uint32_t>::max() (approximately 4 GB).
5755
 *
5756
 * @param length The new maximum URL length in bytes.
5757
 */
5758
void set_max_input_length(uint32_t length);
5759
5760
/**
5761
 * Returns the current maximum allowed length for URLs.
5762
 *
5763
 * @return The current maximum URL length in bytes.
5764
 */
5765
uint32_t get_max_input_length();
5766
5767
}  // namespace ada
5768
5769
#endif  // ADA_IMPLEMENTATION_H
5770
/* end file include/ada/implementation.h */
5771
5772
#include <cstdint>
5773
#include <optional>
5774
#include <ostream>
5775
#include <string>
5776
#include <string_view>
5777
#include <unordered_map>
5778
#include <utility>
5779
#include <variant>
5780
#include <vector>
5781
5782
#if ADA_TESTING
5783
#include <iostream>
5784
#endif  // ADA_TESTING
5785
5786
#if ADA_INCLUDE_URL_PATTERN
5787
namespace ada {
5788
5789
enum class url_pattern_part_type : uint8_t {
5790
  // The part represents a simple fixed text string.
5791
  FIXED_TEXT,
5792
  // The part represents a matching group with a custom regular expression.
5793
  REGEXP,
5794
  // The part represents a matching group that matches code points up to the
5795
  // next separator code point. This is typically used for a named group like
5796
  // ":foo" that does not have a custom regular expression.
5797
  SEGMENT_WILDCARD,
5798
  // The part represents a matching group that greedily matches all code points.
5799
  // This is typically used for the "*" wildcard matching group.
5800
  FULL_WILDCARD,
5801
};
5802
5803
// Pattern type for fast-path matching optimization.
5804
// This allows skipping expensive regex evaluation for common simple patterns.
5805
enum class url_pattern_component_type : uint8_t {
5806
  // Pattern is "^$" - only matches empty string
5807
  EMPTY,
5808
  // Pattern is "^<literal>$" - exact string match (no regex needed)
5809
  EXACT_MATCH,
5810
  // Pattern is "^(.*)$" - matches anything (full wildcard)
5811
  FULL_WILDCARD,
5812
  // Pattern requires actual regex evaluation
5813
  REGEXP,
5814
};
5815
5816
enum class url_pattern_part_modifier : uint8_t {
5817
  // The part does not have a modifier.
5818
  none,
5819
  // The part has an optional modifier indicated by the U+003F (?) code point.
5820
  optional,
5821
  // The part has a "zero or more" modifier indicated by the U+002A (*) code
5822
  // point.
5823
  zero_or_more,
5824
  // The part has a "one or more" modifier indicated by the U+002B (+) code
5825
  // point.
5826
  one_or_more,
5827
};
5828
5829
// @see https://urlpattern.spec.whatwg.org/#part
5830
class url_pattern_part {
5831
 public:
5832
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5833
                   url_pattern_part_modifier _modifier)
5834
0
      : type(_type), value(std::move(_value)), modifier(_modifier) {}
5835
5836
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5837
                   url_pattern_part_modifier _modifier, std::string&& _name,
5838
                   std::string&& _prefix, std::string&& _suffix)
5839
0
      : type(_type),
5840
0
        value(std::move(_value)),
5841
0
        modifier(_modifier),
5842
0
        name(std::move(_name)),
5843
0
        prefix(std::move(_prefix)),
5844
0
        suffix(std::move(_suffix)) {}
5845
  // A part has an associated type, a string, which must be set upon creation.
5846
  url_pattern_part_type type;
5847
  // A part has an associated value, a string, which must be set upon creation.
5848
  std::string value;
5849
  // A part has an associated modifier a string, which must be set upon
5850
  // creation.
5851
  url_pattern_part_modifier modifier;
5852
  // A part has an associated name, a string, initially the empty string.
5853
  std::string name{};
5854
  // A part has an associated prefix, a string, initially the empty string.
5855
  std::string prefix{};
5856
  // A part has an associated suffix, a string, initially the empty string.
5857
  std::string suffix{};
5858
5859
  inline bool is_regexp() const noexcept;
5860
};
5861
5862
// @see https://urlpattern.spec.whatwg.org/#options-header
5863
struct url_pattern_compile_component_options {
5864
  url_pattern_compile_component_options() = default;
5865
  explicit url_pattern_compile_component_options(
5866
      std::optional<char> new_delimiter = std::nullopt,
5867
      std::optional<char> new_prefix = std::nullopt) noexcept
5868
6
      : delimiter(new_delimiter), prefix(new_prefix) {}
5869
5870
  inline std::string_view get_delimiter() const ada_warn_unused;
5871
  inline std::string_view get_prefix() const ada_warn_unused;
5872
5873
  // @see https://urlpattern.spec.whatwg.org/#options-ignore-case
5874
  bool ignore_case = false;
5875
5876
  static url_pattern_compile_component_options DEFAULT;
5877
  static url_pattern_compile_component_options HOSTNAME;
5878
  static url_pattern_compile_component_options PATHNAME;
5879
5880
 private:
5881
  // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point
5882
  std::optional<char> delimiter{};
5883
  // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point
5884
  std::optional<char> prefix{};
5885
};
5886
5887
// The default options is an options struct with delimiter code point set to
5888
// the empty string and prefix code point set to the empty string.
5889
inline url_pattern_compile_component_options
5890
    url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt);
5891
5892
// The hostname options is an options struct with delimiter code point set
5893
// "." and prefix code point set to the empty string.
5894
inline url_pattern_compile_component_options
5895
    url_pattern_compile_component_options::HOSTNAME('.', std::nullopt);
5896
5897
// The pathname options is an options struct with delimiter code point set
5898
// "/" and prefix code point set to "/".
5899
inline url_pattern_compile_component_options
5900
    url_pattern_compile_component_options::PATHNAME('/', '/');
5901
5902
// A struct providing the URLPattern matching results for a single
5903
// URL component. The URLPatternComponentResult is only ever used
5904
// as a member attribute of a URLPatternResult struct. The
5905
// URLPatternComponentResult API is defined as part of the URLPattern
5906
// specification.
5907
struct url_pattern_component_result {
5908
  std::string input;
5909
  std::unordered_map<std::string, std::optional<std::string>> groups;
5910
5911
  bool operator==(const url_pattern_component_result&) const;
5912
5913
#if ADA_TESTING
5914
  friend void PrintTo(const url_pattern_component_result& result,
5915
                      std::ostream* os) {
5916
    *os << "input: '" << result.input << "', group: ";
5917
    for (const auto& group : result.groups) {
5918
      *os << "(" << group.first << ", " << group.second.value_or("undefined")
5919
          << ") ";
5920
    }
5921
  }
5922
#endif  // ADA_TESTING
5923
};
5924
5925
template <url_pattern_regex::regex_concept regex_provider>
5926
class url_pattern_component {
5927
 public:
5928
  url_pattern_component() = default;
5929
5930
  // This function explicitly takes a std::string because it is moved.
5931
  // To avoid unnecessary copy, move each value while calling the constructor.
5932
  url_pattern_component(std::string&& new_pattern,
5933
                        typename regex_provider::regex_type&& new_regexp,
5934
                        std::vector<std::string>&& new_group_name_list,
5935
                        bool new_has_regexp_groups,
5936
                        url_pattern_component_type new_type,
5937
                        std::string&& new_exact_match_value = {})
5938
      : regexp(std::move(new_regexp)),
5939
        pattern(std::move(new_pattern)),
5940
        group_name_list(std::move(new_group_name_list)),
5941
        exact_match_value(std::move(new_exact_match_value)),
5942
        has_regexp_groups(new_has_regexp_groups),
5943
        type(new_type) {}
5944
5945
  // @see https://urlpattern.spec.whatwg.org/#compile-a-component
5946
  template <url_pattern_encoding_callback F>
5947
  static tl::expected<url_pattern_component, errors> compile(
5948
      std::string_view input, F& encoding_callback,
5949
      url_pattern_compile_component_options& options);
5950
5951
  // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result
5952
  url_pattern_component_result create_component_match_result(
5953
      std::string&& input,
5954
      std::vector<std::optional<std::string>>&& exec_result);
5955
5956
  // Fast path test that returns true/false without constructing result groups.
5957
  // Uses cached pattern type to skip regex evaluation for simple patterns.
5958
  // Note: Not marked noexcept because the regex provider may throw.
5959
  bool fast_test(std::string_view input) const;
5960
5961
  // Fast path match that returns capture groups without regex for simple
5962
  // patterns. Returns nullopt if pattern doesn't match, otherwise returns
5963
  // capture groups.
5964
  std::optional<std::vector<std::optional<std::string>>> fast_match(
5965
      std::string_view input) const;
5966
5967
#if ADA_TESTING
5968
  friend void PrintTo(const url_pattern_component& component,
5969
                      std::ostream* os) {
5970
    *os << "pattern: '" << component.pattern
5971
        << "', has_regexp_groups: " << component.has_regexp_groups
5972
        << "group_name_list: ";
5973
    for (const auto& name : component.group_name_list) {
5974
      *os << name << ", ";
5975
    }
5976
  }
5977
#endif  // ADA_TESTING
5978
5979
  typename regex_provider::regex_type regexp{};
5980
  std::string pattern{};
5981
  std::vector<std::string> group_name_list{};
5982
  // For EXACT_MATCH type: the literal string to compare against
5983
  std::string exact_match_value{};
5984
  bool has_regexp_groups = false;
5985
  // Cached pattern type for fast-path optimization
5986
  url_pattern_component_type type = url_pattern_component_type::REGEXP;
5987
};
5988
5989
// A URLPattern input can be either a string or a URLPatternInit object.
5990
// If it is a string, it must be a valid UTF-8 string.
5991
using url_pattern_input = std::variant<std::string_view, url_pattern_init>;
5992
5993
// A struct providing the URLPattern matching results for all
5994
// components of a URL. The URLPatternResult API is defined as
5995
// part of the URLPattern specification.
5996
struct url_pattern_result {
5997
  std::vector<url_pattern_input> inputs;
5998
  url_pattern_component_result protocol;
5999
  url_pattern_component_result username;
6000
  url_pattern_component_result password;
6001
  url_pattern_component_result hostname;
6002
  url_pattern_component_result port;
6003
  url_pattern_component_result pathname;
6004
  url_pattern_component_result search;
6005
  url_pattern_component_result hash;
6006
};
6007
6008
struct url_pattern_options {
6009
  bool ignore_case = false;
6010
6011
#if ADA_TESTING
6012
  friend void PrintTo(const url_pattern_options& options, std::ostream* os) {
6013
    *os << "ignore_case: '" << options.ignore_case;
6014
  }
6015
#endif  // ADA_TESTING
6016
};
6017
6018
/**
6019
 * @brief URL pattern matching class implementing the URLPattern API.
6020
 *
6021
 * URLPattern provides a way to match URLs against patterns with wildcards
6022
 * and named capture groups. It's useful for routing, URL-based dispatching,
6023
 * and URL validation.
6024
 *
6025
 * Pattern syntax supports:
6026
 * - Literal text matching
6027
 * - Named groups: `:name` (matches up to the next separator)
6028
 * - Wildcards: `*` (matches everything)
6029
 * - Custom regex: `(pattern)`
6030
 * - Optional segments: `:name?`
6031
 * - Repeated segments: `:name+`, `:name*`
6032
 *
6033
 * @tparam regex_provider The regex implementation to use for pattern matching.
6034
 *         Must satisfy the url_pattern_regex::regex_concept.
6035
 *
6036
 * @note All string inputs must be valid UTF-8.
6037
 *
6038
 * @see https://urlpattern.spec.whatwg.org/
6039
 */
6040
template <url_pattern_regex::regex_concept regex_provider>
6041
class url_pattern {
6042
 public:
6043
  url_pattern() = default;
6044
6045
  /**
6046
   * If non-null, base_url must pointer at a valid UTF-8 string.
6047
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec
6048
   */
6049
  result<std::optional<url_pattern_result>> exec(
6050
      const url_pattern_input& input,
6051
      const std::string_view* base_url = nullptr);
6052
6053
  /**
6054
   * If non-null, base_url must pointer at a valid UTF-8 string.
6055
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
6056
   */
6057
  result<bool> test(const url_pattern_input& input,
6058
                    const std::string_view* base_url = nullptr);
6059
6060
  /**
6061
   * @see https://urlpattern.spec.whatwg.org/#url-pattern-match
6062
   * This function expects a valid UTF-8 string if input is a string.
6063
   */
6064
  result<std::optional<url_pattern_result>> match(
6065
      const url_pattern_input& input,
6066
      const std::string_view* base_url_string = nullptr);
6067
6068
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol
6069
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
6070
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username
6071
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
6072
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password
6073
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
6074
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname
6075
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
6076
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port
6077
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
6078
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname
6079
  [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound;
6080
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search
6081
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
6082
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash
6083
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
6084
6085
  // If ignoreCase is true, the JavaScript regular expression created for each
6086
  // pattern must use the `vi` flag. Otherwise, they must use the `v` flag.
6087
  [[nodiscard]] bool ignore_case() const;
6088
6089
  // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups
6090
  [[nodiscard]] bool has_regexp_groups() const;
6091
6092
  // Helper to test all components at once. Returns true if all match.
6093
  [[nodiscard]] bool test_components(
6094
      std::string_view protocol, std::string_view username,
6095
      std::string_view password, std::string_view hostname,
6096
      std::string_view port, std::string_view pathname, std::string_view search,
6097
      std::string_view hash) const;
6098
6099
#if ADA_TESTING
6100
  friend void PrintTo(const url_pattern& c, std::ostream* os) {
6101
    *os << "protocol_component: '" << c.get_protocol() << ", ";
6102
    *os << "username_component: '" << c.get_username() << ", ";
6103
    *os << "password_component: '" << c.get_password() << ", ";
6104
    *os << "hostname_component: '" << c.get_hostname() << ", ";
6105
    *os << "port_component: '" << c.get_port() << ", ";
6106
    *os << "pathname_component: '" << c.get_pathname() << ", ";
6107
    *os << "search_component: '" << c.get_search() << ", ";
6108
    *os << "hash_component: '" << c.get_hash();
6109
  }
6110
#endif  // ADA_TESTING
6111
6112
  template <url_pattern_regex::regex_concept P>
6113
  friend tl::expected<url_pattern<P>, errors> parser::parse_url_pattern_impl(
6114
      std::variant<std::string_view, url_pattern_init>&& input,
6115
      const std::string_view* base_url, const url_pattern_options* options);
6116
6117
  /**
6118
   * @private
6119
   * We can not make this private due to a LLVM bug.
6120
   * Ref: https://github.com/ada-url/ada/pull/859
6121
   */
6122
  url_pattern_component<regex_provider> protocol_component{};
6123
  /**
6124
   * @private
6125
   * We can not make this private due to a LLVM bug.
6126
   * Ref: https://github.com/ada-url/ada/pull/859
6127
   */
6128
  url_pattern_component<regex_provider> username_component{};
6129
  /**
6130
   * @private
6131
   * We can not make this private due to a LLVM bug.
6132
   * Ref: https://github.com/ada-url/ada/pull/859
6133
   */
6134
  url_pattern_component<regex_provider> password_component{};
6135
  /**
6136
   * @private
6137
   * We can not make this private due to a LLVM bug.
6138
   * Ref: https://github.com/ada-url/ada/pull/859
6139
   */
6140
  url_pattern_component<regex_provider> hostname_component{};
6141
  /**
6142
   * @private
6143
   * We can not make this private due to a LLVM bug.
6144
   * Ref: https://github.com/ada-url/ada/pull/859
6145
   */
6146
  url_pattern_component<regex_provider> port_component{};
6147
  /**
6148
   * @private
6149
   * We can not make this private due to a LLVM bug.
6150
   * Ref: https://github.com/ada-url/ada/pull/859
6151
   */
6152
  url_pattern_component<regex_provider> pathname_component{};
6153
  /**
6154
   * @private
6155
   * We can not make this private due to a LLVM bug.
6156
   * Ref: https://github.com/ada-url/ada/pull/859
6157
   */
6158
  url_pattern_component<regex_provider> search_component{};
6159
  /**
6160
   * @private
6161
   * We can not make this private due to a LLVM bug.
6162
   * Ref: https://github.com/ada-url/ada/pull/859
6163
   */
6164
  url_pattern_component<regex_provider> hash_component{};
6165
  /**
6166
   * @private
6167
   * We can not make this private due to a LLVM bug.
6168
   * Ref: https://github.com/ada-url/ada/pull/859
6169
   */
6170
  bool ignore_case_ = false;
6171
};
6172
}  // namespace ada
6173
#endif  // ADA_INCLUDE_URL_PATTERN
6174
#endif
6175
/* end file include/ada/url_pattern.h */
6176
/* begin file include/ada/url_pattern_helpers.h */
6177
/**
6178
 * @file url_pattern_helpers.h
6179
 * @brief Declaration for the URLPattern helpers.
6180
 */
6181
#ifndef ADA_URL_PATTERN_HELPERS_H
6182
#define ADA_URL_PATTERN_HELPERS_H
6183
6184
6185
#include <cstddef>
6186
#include <cstdint>
6187
#include <optional>
6188
#include <string>
6189
#include <string_view>
6190
#include <tuple>
6191
#include <unordered_set>
6192
#include <utility>
6193
#include <vector>
6194
6195
#if ADA_INCLUDE_URL_PATTERN
6196
namespace ada {
6197
enum class errors : uint8_t;
6198
}
6199
6200
namespace ada::url_pattern_helpers {
6201
6202
// @see https://urlpattern.spec.whatwg.org/#token
6203
enum class token_type : uint8_t {
6204
  INVALID_CHAR,    // 0
6205
  OPEN,            // 1
6206
  CLOSE,           // 2
6207
  REGEXP,          // 3
6208
  NAME,            // 4
6209
  CHAR,            // 5
6210
  ESCAPED_CHAR,    // 6
6211
  OTHER_MODIFIER,  // 7
6212
  ASTERISK,        // 8
6213
  END,             // 9
6214
};
6215
6216
#ifdef ADA_TESTING
6217
std::string to_string(token_type type);
6218
#endif  // ADA_TESTING
6219
6220
// @see https://urlpattern.spec.whatwg.org/#tokenize-policy
6221
enum class token_policy {
6222
  strict,
6223
  lenient,
6224
};
6225
6226
// @see https://urlpattern.spec.whatwg.org/#tokens
6227
class token {
6228
 public:
6229
  token(token_type _type, size_t _index, std::string_view _value)
6230
0
      : type(_type), index(_index), value(_value) {}
6231
6232
  // A token has an associated type, a string, initially "invalid-char".
6233
  token_type type = token_type::INVALID_CHAR;
6234
6235
  // A token has an associated index, a number, initially 0. It is the position
6236
  // of the first code point in the pattern string represented by the token.
6237
  size_t index = 0;
6238
6239
  // A token has an associated value, a string, initially the empty string. It
6240
  // contains the code points from the pattern string represented by the token.
6241
  std::string_view value{};
6242
};
6243
6244
// @see https://urlpattern.spec.whatwg.org/#pattern-parser
6245
template <url_pattern_encoding_callback F>
6246
class url_pattern_parser {
6247
 public:
6248
  url_pattern_parser(F& encoding_callback_,
6249
                     std::string_view segment_wildcard_regexp_)
6250
0
      : encoding_callback(encoding_callback_),
6251
0
        segment_wildcard_regexp(segment_wildcard_regexp_) {}
6252
6253
0
  bool can_continue() const { return index < tokens.size(); }
6254
6255
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token
6256
  token* try_consume_token(token_type type);
6257
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token
6258
  token* try_consume_modifier_token();
6259
  // @see
6260
  // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token
6261
  token* try_consume_regexp_or_wildcard_token(const token* name_token);
6262
  // @see https://urlpattern.spec.whatwg.org/#consume-text
6263
  std::string consume_text();
6264
  // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token
6265
  bool consume_required_token(token_type type);
6266
  // @see
6267
  // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value
6268
  std::optional<errors> maybe_add_part_from_the_pending_fixed_value()
6269
      ada_warn_unused;
6270
  // @see https://urlpattern.spec.whatwg.org/#add-a-part
6271
  std::optional<errors> add_part(std::string_view prefix, token* name_token,
6272
                                 token* regexp_or_wildcard_token,
6273
                                 std::string_view suyffix,
6274
                                 token* modifier_token) ada_warn_unused;
6275
6276
  std::vector<token> tokens{};
6277
  F& encoding_callback;
6278
  std::string segment_wildcard_regexp;
6279
  std::vector<url_pattern_part> parts{};
6280
  std::string pending_fixed_value{};
6281
  size_t index = 0;
6282
  size_t next_numeric_name = 0;
6283
  // Names already taken by a part, for the "is a duplicate name" check in
6284
  // add_part. A linear scan of the part list there is O(n^2) over a pattern
6285
  // with n named parts (e.g. "/*" repeated); this set keeps it amortized O(1).
6286
  std::unordered_set<std::string> seen_names{};
6287
};
6288
6289
// @see https://urlpattern.spec.whatwg.org/#tokenizer
6290
class Tokenizer {
6291
 public:
6292
  explicit Tokenizer(std::string_view new_input, token_policy new_policy)
6293
0
      : input(new_input), policy(new_policy) {}
6294
6295
  // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point
6296
  constexpr void get_next_code_point();
6297
6298
  // True when the most recent decoded unit was malformed UTF-8.
6299
0
  bool had_invalid_code_point() const { return invalid_code_point; }
6300
6301
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
6302
  constexpr void seek_and_get_next_code_point(size_t index);
6303
6304
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
6305
6306
  void add_token(token_type type, size_t next_position, size_t value_position,
6307
                 size_t value_length);
6308
6309
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
6310
  void add_token_with_default_length(token_type type, size_t next_position,
6311
                                     size_t value_position);
6312
6313
  // @see
6314
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
6315
  void add_token_with_defaults(token_type type);
6316
6317
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
6318
  std::optional<errors> process_tokenizing_error(
6319
      size_t next_position, size_t value_position) ada_warn_unused;
6320
6321
  friend tl::expected<std::vector<token>, errors> tokenize(
6322
      std::string_view input, token_policy policy);
6323
6324
 private:
6325
  // has an associated input, a pattern string, initially the empty string.
6326
  std::string_view input;
6327
  // has an associated policy, a tokenize policy, initially "strict".
6328
  token_policy policy;
6329
  // has an associated token list, a token list, initially an empty list.
6330
  std::vector<token> token_list{};
6331
  // has an associated index, a number, initially 0.
6332
  size_t index = 0;
6333
  // has an associated next index, a number, initially 0.
6334
  size_t next_index = 0;
6335
  // has an associated code point, a Unicode code point, initially null.
6336
  char32_t code_point{};
6337
  // Tracks whether the last decoded code point was malformed UTF-8.
6338
  bool invalid_code_point = false;
6339
};
6340
6341
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
6342
template <url_pattern_regex::regex_concept regex_provider>
6343
struct constructor_string_parser {
6344
  explicit constructor_string_parser(std::string_view new_input,
6345
                                     std::vector<token>&& new_token_list)
6346
      : input(new_input), token_list(std::move(new_token_list)) {}
6347
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
6348
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
6349
6350
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
6351
  enum class State {
6352
    INIT,
6353
    PROTOCOL,
6354
    AUTHORITY,
6355
    USERNAME,
6356
    PASSWORD,
6357
    HOSTNAME,
6358
    PORT,
6359
    PATHNAME,
6360
    SEARCH,
6361
    HASH,
6362
    DONE,
6363
  };
6364
6365
  // @see
6366
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6367
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6368
6369
 private:
6370
  // @see https://urlpattern.spec.whatwg.org/#rewind
6371
  constexpr void rewind();
6372
6373
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6374
  constexpr bool is_hash_prefix();
6375
6376
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6377
  constexpr bool is_search_prefix();
6378
6379
  // @see https://urlpattern.spec.whatwg.org/#change-state
6380
  void change_state(State state, size_t skip);
6381
6382
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6383
  constexpr bool is_group_open() const;
6384
6385
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6386
  constexpr bool is_group_close() const;
6387
6388
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6389
  constexpr bool is_protocol_suffix() const;
6390
6391
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6392
  constexpr bool next_is_authority_slashes() const;
6393
6394
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6395
  constexpr bool is_an_identity_terminator() const;
6396
6397
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6398
  constexpr bool is_pathname_start() const;
6399
6400
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6401
  constexpr bool is_password_prefix() const;
6402
6403
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6404
  constexpr bool is_an_ipv6_open() const;
6405
6406
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6407
  constexpr bool is_an_ipv6_close() const;
6408
6409
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6410
  constexpr bool is_port_prefix() const;
6411
6412
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6413
  constexpr bool is_non_special_pattern_char(size_t index,
6414
                                             uint32_t value) const;
6415
6416
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6417
  constexpr const token* get_safe_token(size_t index) const;
6418
6419
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6420
  std::string make_component_string();
6421
  // has an associated input, a string, which must be set upon creation.
6422
  std::string_view input;
6423
  // has an associated token list, a token list, which must be set upon
6424
  // creation.
6425
  std::vector<token> token_list;
6426
  // has an associated result, a URLPatternInit, initially set to a new
6427
  // URLPatternInit.
6428
  url_pattern_init result{};
6429
  // has an associated component start, a number, initially set to 0.
6430
  size_t component_start = 0;
6431
  // has an associated token index, a number, initially set to 0.
6432
  size_t token_index = 0;
6433
  // has an associated token increment, a number, initially set to 1.
6434
  size_t token_increment = 1;
6435
  // has an associated group depth, a number, initially set to 0.
6436
  size_t group_depth = 0;
6437
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6438
  // 0.
6439
  size_t hostname_ipv6_bracket_depth = 0;
6440
  // has an associated protocol matches a special scheme flag, a boolean,
6441
  // initially set to false.
6442
  bool protocol_matches_a_special_scheme_flag = false;
6443
  // has an associated state, a string, initially set to "init".
6444
  State state = State::INIT;
6445
};
6446
6447
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6448
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6449
6450
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6451
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6452
6453
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6454
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6455
6456
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6457
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6458
6459
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6460
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6461
    std::string_view input);
6462
6463
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6464
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6465
6466
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6467
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6468
    std::string_view input, std::string_view protocol);
6469
6470
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6471
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6472
6473
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6474
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6475
    std::string_view input);
6476
6477
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6478
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6479
6480
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6481
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6482
6483
// @see https://urlpattern.spec.whatwg.org/#tokenize
6484
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6485
                                                  token_policy policy);
6486
6487
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6488
std::string process_base_url_string(std::string_view input,
6489
                                    url_pattern_init::process_type type);
6490
6491
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6492
std::string escape_pattern_string(std::string_view input);
6493
6494
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6495
std::string escape_regexp_string(std::string_view input);
6496
6497
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6498
constexpr bool is_absolute_pathname(
6499
    std::string_view input, url_pattern_init::process_type type) noexcept;
6500
6501
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6502
template <url_pattern_encoding_callback F>
6503
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6504
    std::string_view input, url_pattern_compile_component_options& options,
6505
    F& encoding_callback);
6506
6507
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6508
std::string generate_pattern_string(
6509
    std::vector<url_pattern_part>& part_list,
6510
    url_pattern_compile_component_options& options);
6511
6512
// @see
6513
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6514
std::tuple<std::string, std::vector<std::string>>
6515
generate_regular_expression_and_name_list(
6516
    const std::vector<url_pattern_part>& part_list,
6517
    url_pattern_compile_component_options options);
6518
6519
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6520
bool is_ipv6_address(std::string_view input) noexcept;
6521
6522
// @see
6523
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6524
template <url_pattern_regex::regex_concept regex_provider>
6525
bool protocol_component_matches_special_scheme(
6526
    ada::url_pattern_component<regex_provider>& input);
6527
6528
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6529
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6530
6531
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6532
std::string generate_segment_wildcard_regexp(
6533
    url_pattern_compile_component_options options);
6534
6535
}  // namespace ada::url_pattern_helpers
6536
#endif  // ADA_INCLUDE_URL_PATTERN
6537
#endif
6538
/* end file include/ada/url_pattern_helpers.h */
6539
6540
#include <cstdint>
6541
#include <string>
6542
#include <string_view>
6543
#include <utility>
6544
#include <variant>
6545
6546
namespace ada::parser {
6547
#if ADA_INCLUDE_URL_PATTERN
6548
template <url_pattern_regex::regex_concept regex_provider>
6549
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6550
    std::variant<std::string_view, url_pattern_init>&& input,
6551
    const std::string_view* base_url, const url_pattern_options* options) {
6552
  // Let init be null.
6553
  url_pattern_init init;
6554
6555
  // If input is a scalar value string then:
6556
  if (std::holds_alternative<std::string_view>(input)) {
6557
    // Set init to the result of running parse a constructor string given input.
6558
    auto parse_result =
6559
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6560
            std::get<std::string_view>(input));
6561
    if (!parse_result) {
6562
      ada_log("constructor_string_parser::parse failed");
6563
      return tl::unexpected(parse_result.error());
6564
    }
6565
    init = std::move(*parse_result);
6566
    // If baseURL is null and init["protocol"] does not exist, then throw a
6567
    // TypeError.
6568
    if (!base_url && !init.protocol) {
6569
      ada_log("base url is null and protocol is not set");
6570
      return tl::unexpected(errors::type_error);
6571
    }
6572
6573
    // If baseURL is not null, set init["baseURL"] to baseURL.
6574
    if (base_url) {
6575
      init.base_url = std::string(*base_url);
6576
    }
6577
  } else {
6578
    // Assert: input is a URLPatternInit.
6579
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6580
    // If baseURL is not null, then throw a TypeError.
6581
    if (base_url) {
6582
      ada_log("base url is not null");
6583
      return tl::unexpected(errors::type_error);
6584
    }
6585
    // Optimization: Avoid copy by moving the input value.
6586
    // Set init to input.
6587
    init = std::move(std::get<url_pattern_init>(input));
6588
  }
6589
6590
  // Let processedInit be the result of process a URLPatternInit given init,
6591
  // "pattern", null, null, null, null, null, null, null, and null.
6592
  auto processed_init =
6593
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6594
  if (!processed_init) {
6595
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6596
    return tl::unexpected(processed_init.error());
6597
  }
6598
6599
  // For each componentName of  "protocol", "username", "password", "hostname",
6600
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6601
  // not exist, then set processedInit[componentName] to "*".
6602
  ADA_ASSERT_TRUE(processed_init.has_value());
6603
  if (!processed_init->protocol) processed_init->protocol = "*";
6604
  if (!processed_init->username) processed_init->username = "*";
6605
  if (!processed_init->password) processed_init->password = "*";
6606
  if (!processed_init->hostname) processed_init->hostname = "*";
6607
  if (!processed_init->port) processed_init->port = "*";
6608
  if (!processed_init->pathname) processed_init->pathname = "*";
6609
  if (!processed_init->search) processed_init->search = "*";
6610
  if (!processed_init->hash) processed_init->hash = "*";
6611
6612
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6613
  ada_log("-- processed_init->username: ", processed_init->username.value());
6614
  ada_log("-- processed_init->password: ", processed_init->password.value());
6615
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6616
  ada_log("-- processed_init->port: ", processed_init->port.value());
6617
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6618
  ada_log("-- processed_init->search: ", processed_init->search.value());
6619
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6620
6621
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6622
  // is a string which represents its corresponding default port in radix-10
6623
  // using ASCII digits then set processedInit["port"] to the empty string.
6624
  // TODO: Optimization opportunity.
6625
  if (scheme::is_special(*processed_init->protocol)) {
6626
    // file is the only special scheme with no default port; get_special_port
6627
    // returns 0 as its sentinel, so a literal "0" port must not be mistaken for
6628
    // a default and dropped.
6629
    uint16_t default_port = scheme::get_special_port(*processed_init->protocol);
6630
    if (default_port != 0 &&
6631
        std::to_string(default_port) == processed_init->port.value()) {
6632
      processed_init->port->clear();
6633
    }
6634
  }
6635
6636
  // Let urlPattern be a new URL pattern.
6637
  url_pattern<regex_provider> url_pattern_{};
6638
6639
  // Set urlPattern's protocol component to the result of compiling a component
6640
  // given processedInit["protocol"], canonicalize a protocol, and default
6641
  // options.
6642
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6643
      processed_init->protocol.value(),
6644
      url_pattern_helpers::canonicalize_protocol,
6645
      url_pattern_compile_component_options::DEFAULT);
6646
  if (!protocol_component) {
6647
    ada_log("url_pattern_component::compile failed for protocol ",
6648
            processed_init->protocol.value());
6649
    return tl::unexpected(protocol_component.error());
6650
  }
6651
  url_pattern_.protocol_component = std::move(*protocol_component);
6652
6653
  // Set urlPattern's username component to the result of compiling a component
6654
  // given processedInit["username"], canonicalize a username, and default
6655
  // options.
6656
  auto username_component = url_pattern_component<regex_provider>::compile(
6657
      processed_init->username.value(),
6658
      url_pattern_helpers::canonicalize_username,
6659
      url_pattern_compile_component_options::DEFAULT);
6660
  if (!username_component) {
6661
    ada_log("url_pattern_component::compile failed for username ",
6662
            processed_init->username.value());
6663
    return tl::unexpected(username_component.error());
6664
  }
6665
  url_pattern_.username_component = std::move(*username_component);
6666
6667
  // Set urlPattern's password component to the result of compiling a component
6668
  // given processedInit["password"], canonicalize a password, and default
6669
  // options.
6670
  auto password_component = url_pattern_component<regex_provider>::compile(
6671
      processed_init->password.value(),
6672
      url_pattern_helpers::canonicalize_password,
6673
      url_pattern_compile_component_options::DEFAULT);
6674
  if (!password_component) {
6675
    ada_log("url_pattern_component::compile failed for password ",
6676
            processed_init->password.value());
6677
    return tl::unexpected(password_component.error());
6678
  }
6679
  url_pattern_.password_component = std::move(*password_component);
6680
6681
  // TODO: Optimization opportunity. The following if statement can be
6682
  // simplified.
6683
  // If the result running hostname pattern is an IPv6 address given
6684
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6685
  // to the result of compiling a component given processedInit["hostname"],
6686
  // canonicalize an IPv6 hostname, and hostname options.
6687
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6688
    ada_log("processed_init->hostname is ipv6 address");
6689
    // then set urlPattern's hostname component to the result of compiling a
6690
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6691
    // and hostname options.
6692
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6693
        processed_init->hostname.value(),
6694
        url_pattern_helpers::canonicalize_ipv6_hostname,
6695
        url_pattern_compile_component_options::DEFAULT);
6696
    if (!hostname_component) {
6697
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6698
              processed_init->hostname.value());
6699
      return tl::unexpected(hostname_component.error());
6700
    }
6701
    url_pattern_.hostname_component = std::move(*hostname_component);
6702
  } else {
6703
    // Otherwise, set urlPattern's hostname component to the result of compiling
6704
    // a component given processedInit["hostname"], canonicalize a hostname, and
6705
    // hostname options.
6706
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6707
        processed_init->hostname.value(),
6708
        url_pattern_helpers::canonicalize_hostname,
6709
        url_pattern_compile_component_options::HOSTNAME);
6710
    if (!hostname_component) {
6711
      ada_log("url_pattern_component::compile failed for hostname ",
6712
              processed_init->hostname.value());
6713
      return tl::unexpected(hostname_component.error());
6714
    }
6715
    url_pattern_.hostname_component = std::move(*hostname_component);
6716
  }
6717
6718
  // Set urlPattern's port component to the result of compiling a component
6719
  // given processedInit["port"], canonicalize a port, and default options.
6720
  auto port_component = url_pattern_component<regex_provider>::compile(
6721
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6722
      url_pattern_compile_component_options::DEFAULT);
6723
  if (!port_component) {
6724
    ada_log("url_pattern_component::compile failed for port ",
6725
            processed_init->port.value());
6726
    return tl::unexpected(port_component.error());
6727
  }
6728
  url_pattern_.port_component = std::move(*port_component);
6729
6730
  // Let compileOptions be a copy of the default options with the ignore case
6731
  // property set to options["ignoreCase"].
6732
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6733
  if (options) {
6734
    compile_options.ignore_case = options->ignore_case;
6735
    // Recorded on the pattern so that url_pattern::ignore_case() reports the
6736
    // option it was created with.
6737
    url_pattern_.ignore_case_ = options->ignore_case;
6738
  }
6739
6740
  // TODO: Optimization opportunity: Simplify this if statement.
6741
  // If the result of running protocol component matches a special scheme given
6742
  // urlPattern's protocol component is true, then:
6743
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6744
          regex_provider>(url_pattern_.protocol_component)) {
6745
    // Let pathCompileOptions be copy of the pathname options with the ignore
6746
    // case property set to options["ignoreCase"].
6747
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6748
    if (options) {
6749
      path_compile_options.ignore_case = options->ignore_case;
6750
    }
6751
6752
    // Set urlPattern's pathname component to the result of compiling a
6753
    // component given processedInit["pathname"], canonicalize a pathname, and
6754
    // pathCompileOptions.
6755
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6756
        processed_init->pathname.value(),
6757
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6758
    if (!pathname_component) {
6759
      ada_log("url_pattern_component::compile failed for pathname ",
6760
              processed_init->pathname.value());
6761
      return tl::unexpected(pathname_component.error());
6762
    }
6763
    url_pattern_.pathname_component = std::move(*pathname_component);
6764
  } else {
6765
    // Otherwise set urlPattern's pathname component to the result of compiling
6766
    // a component given processedInit["pathname"], canonicalize an opaque
6767
    // pathname, and compileOptions.
6768
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6769
        processed_init->pathname.value(),
6770
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6771
    if (!pathname_component) {
6772
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6773
              processed_init->pathname.value());
6774
      return tl::unexpected(pathname_component.error());
6775
    }
6776
    url_pattern_.pathname_component = std::move(*pathname_component);
6777
  }
6778
6779
  // Set urlPattern's search component to the result of compiling a component
6780
  // given processedInit["search"], canonicalize a search, and compileOptions.
6781
  auto search_component = url_pattern_component<regex_provider>::compile(
6782
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6783
      compile_options);
6784
  if (!search_component) {
6785
    ada_log("url_pattern_component::compile failed for search ",
6786
            processed_init->search.value());
6787
    return tl::unexpected(search_component.error());
6788
  }
6789
  url_pattern_.search_component = std::move(*search_component);
6790
6791
  // Set urlPattern's hash component to the result of compiling a component
6792
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6793
  auto hash_component = url_pattern_component<regex_provider>::compile(
6794
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6795
      compile_options);
6796
  if (!hash_component) {
6797
    ada_log("url_pattern_component::compile failed for hash ",
6798
            processed_init->hash.value());
6799
    return tl::unexpected(hash_component.error());
6800
  }
6801
  url_pattern_.hash_component = std::move(*hash_component);
6802
6803
  // Return urlPattern.
6804
  return url_pattern_;
6805
}
6806
#endif  // ADA_INCLUDE_URL_PATTERN
6807
6808
}  // namespace ada::parser
6809
6810
#endif  // ADA_PARSER_INL_H
6811
/* end file include/ada/parser-inl.h */
6812
/* begin file include/ada/scheme-inl.h */
6813
/**
6814
 * @file scheme-inl.h
6815
 * @brief Definitions for the URL scheme.
6816
 */
6817
#ifndef ADA_SCHEME_INL_H
6818
#define ADA_SCHEME_INL_H
6819
6820
6821
#include <cstddef>
6822
#include <cstdint>
6823
#include <string_view>
6824
6825
namespace ada::scheme {
6826
6827
/**
6828
 * @namespace ada::scheme::details
6829
 * @brief Includes the definitions for scheme specific entities
6830
 */
6831
namespace details {
6832
// for use with is_special and get_special_port
6833
// Spaces, if present, are removed from URL.
6834
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6835
                                                "ftp",  "wss", "file",  " "};
6836
// for use with get_special_port
6837
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6838
6839
// @private
6840
// convert a string_view to a 64-bit integer key for fast comparison
6841
0
constexpr uint64_t make_key(std::string_view sv) {
6842
0
  uint64_t val = 0;
6843
0
  for (size_t i = 0; i < sv.size(); i++)
6844
0
    val |= (uint64_t)(uint8_t)sv[i] << (i * 8);
6845
0
  return val;
6846
0
}
6847
// precomputed keys for the special schemes, indexed by a hash of the input
6848
// string
6849
constexpr uint64_t scheme_keys[] = {
6850
    make_key("http"),   // 0: HTTP
6851
    0,                  // 1: sentinel
6852
    make_key("https"),  // 2: HTTPS
6853
    make_key("ws"),     // 3: WS
6854
    make_key("ftp"),    // 4: FTP
6855
    make_key("wss"),    // 5: WSS
6856
    make_key("file"),   // 6: FILE
6857
    0,                  // 7: sentinel
6858
};
6859
6860
// @private
6861
// branchless load of up to 5 characters into a uint64_t, padding with zeros if
6862
// n < 5
6863
12.1k
inline uint64_t branchless_load5(const char* p, size_t n) {
6864
12.1k
  uint64_t input = (uint8_t)p[0];
6865
12.1k
  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
6866
12.1k
  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
6867
12.1k
  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
6868
12.1k
  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
6869
12.1k
  return input;
6870
12.1k
}
6871
}  // namespace details
6872
6873
/****
6874
 * @private
6875
 * In is_special, get_scheme_type, and get_special_port, we
6876
 * use a standard hashing technique to find the index of the scheme in
6877
 * the is_special_list. The hashing technique is based on the size of
6878
 * the scheme and the first character of the scheme. It ensures that we
6879
 * do at most one string comparison per call. If the protocol is
6880
 * predictible (e.g., it is always "http"), we can get a better average
6881
 * performance by using a simpler approach where we loop and compare
6882
 * scheme with all possible protocols starting with the most likely
6883
 * protocol. Doing multiple comparisons may have a poor worst case
6884
 * performance, however. In this instance, we choose a potentially
6885
 * slightly lower best-case performance for a better worst-case
6886
 * performance. We can revisit this choice at any time.
6887
 *
6888
 * Reference:
6889
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6890
 * More C++ gems 17 (2000).
6891
 *
6892
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6893
 *
6894
 * Reference: https://github.com/ada-url/ada/issues/617
6895
 ****/
6896
6897
0
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6898
0
  if (scheme.empty()) {
6899
0
    return false;
6900
0
  }
6901
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6902
0
  const std::string_view target = details::is_special_list[hash_value];
6903
0
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6904
0
}
6905
0
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6906
0
  if (scheme.empty()) {
6907
0
    return 0;
6908
0
  }
6909
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6910
0
  const std::string_view target = details::is_special_list[hash_value];
6911
0
  if (scheme.size() == target.size() &&
6912
0
      details::branchless_load5(scheme.data(), scheme.size()) ==
6913
0
          details::scheme_keys[hash_value]) {
6914
0
    return details::special_ports[hash_value];
6915
0
  } else {
6916
0
    return 0;
6917
0
  }
6918
0
}
6919
574
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6920
574
  return details::special_ports[int(type)];
6921
574
}
6922
12.1k
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6923
12.1k
  if (scheme.empty()) {
6924
0
    return ada::scheme::NOT_SPECIAL;
6925
0
  }
6926
12.1k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6927
12.1k
  const std::string_view target = details::is_special_list[hash_value];
6928
12.1k
  if (scheme.size() == target.size() &&
6929
12.1k
      details::branchless_load5(scheme.data(), scheme.size()) ==
6930
12.1k
          details::scheme_keys[hash_value]) {
6931
12.1k
    return ada::scheme::type(hash_value);
6932
12.1k
  } else {
6933
0
    return ada::scheme::NOT_SPECIAL;
6934
0
  }
6935
12.1k
}
6936
6937
}  // namespace ada::scheme
6938
6939
#endif  // ADA_SCHEME_INL_H
6940
/* end file include/ada/scheme-inl.h */
6941
/* begin file include/ada/serializers.h */
6942
/**
6943
 * @file serializers.h
6944
 * @brief IP address serialization utilities.
6945
 *
6946
 * This header provides functions for converting IP addresses to their
6947
 * string representations according to the WHATWG URL Standard.
6948
 */
6949
#ifndef ADA_SERIALIZERS_H
6950
#define ADA_SERIALIZERS_H
6951
6952
6953
#include <array>
6954
#include <cstddef>
6955
#include <cstdint>
6956
#include <string>
6957
6958
/**
6959
 * @namespace ada::serializers
6960
 * @brief IP address serialization functions.
6961
 *
6962
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6963
 */
6964
namespace ada::serializers {
6965
6966
/**
6967
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6968
 * Used for :: compression in IPv6 serialization.
6969
 *
6970
 * @param address The 8 16-bit pieces of the IPv6 address.
6971
 * @param[out] compress Index of the start of the longest zero sequence.
6972
 * @param[out] compress_length Length of the longest zero sequence.
6973
 */
6974
void find_longest_sequence_of_ipv6_pieces(
6975
    const std::array<uint16_t, 8>& address, size_t& compress,
6976
    size_t& compress_length) noexcept;
6977
6978
/**
6979
 * Serializes an IPv6 address to its string representation.
6980
 *
6981
 * @param address The 8 16-bit pieces of the IPv6 address.
6982
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6983
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6984
 */
6985
std::string ipv6(const std::array<uint16_t, 8>& address);
6986
6987
/**
6988
 * Serializes an IPv4 address to its dotted-decimal string representation.
6989
 *
6990
 * @param address The 32-bit IPv4 address as an integer.
6991
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6992
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6993
 */
6994
std::string ipv4(uint64_t address);
6995
6996
}  // namespace ada::serializers
6997
6998
#endif  // ADA_SERIALIZERS_H
6999
/* end file include/ada/serializers.h */
7000
/* begin file include/ada/state.h */
7001
/**
7002
 * @file state.h
7003
 * @brief URL parser state machine states.
7004
 *
7005
 * Defines the states used by the URL parsing state machine as specified
7006
 * in the WHATWG URL Standard.
7007
 *
7008
 * @see https://url.spec.whatwg.org/#url-parsing
7009
 */
7010
#ifndef ADA_STATE_H
7011
#define ADA_STATE_H
7012
7013
7014
#include <string>
7015
7016
namespace ada {
7017
7018
/**
7019
 * @brief States in the URL parsing state machine.
7020
 *
7021
 * The URL parser processes input through a sequence of states, each handling
7022
 * a specific part of the URL syntax.
7023
 *
7024
 * @see https://url.spec.whatwg.org/#url-parsing
7025
 */
7026
enum class state {
7027
  /**
7028
   * @see https://url.spec.whatwg.org/#authority-state
7029
   */
7030
  AUTHORITY,
7031
7032
  /**
7033
   * @see https://url.spec.whatwg.org/#scheme-start-state
7034
   */
7035
  SCHEME_START,
7036
7037
  /**
7038
   * @see https://url.spec.whatwg.org/#scheme-state
7039
   */
7040
  SCHEME,
7041
7042
  /**
7043
   * @see https://url.spec.whatwg.org/#host-state
7044
   */
7045
  HOST,
7046
7047
  /**
7048
   * @see https://url.spec.whatwg.org/#no-scheme-state
7049
   */
7050
  NO_SCHEME,
7051
7052
  /**
7053
   * @see https://url.spec.whatwg.org/#fragment-state
7054
   */
7055
  FRAGMENT,
7056
7057
  /**
7058
   * @see https://url.spec.whatwg.org/#relative-state
7059
   */
7060
  RELATIVE_SCHEME,
7061
7062
  /**
7063
   * @see https://url.spec.whatwg.org/#relative-slash-state
7064
   */
7065
  RELATIVE_SLASH,
7066
7067
  /**
7068
   * @see https://url.spec.whatwg.org/#file-state
7069
   */
7070
  FILE,
7071
7072
  /**
7073
   * @see https://url.spec.whatwg.org/#file-host-state
7074
   */
7075
  FILE_HOST,
7076
7077
  /**
7078
   * @see https://url.spec.whatwg.org/#file-slash-state
7079
   */
7080
  FILE_SLASH,
7081
7082
  /**
7083
   * @see https://url.spec.whatwg.org/#path-or-authority-state
7084
   */
7085
  PATH_OR_AUTHORITY,
7086
7087
  /**
7088
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
7089
   */
7090
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
7091
7092
  /**
7093
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
7094
   */
7095
  SPECIAL_AUTHORITY_SLASHES,
7096
7097
  /**
7098
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
7099
   */
7100
  SPECIAL_RELATIVE_OR_AUTHORITY,
7101
7102
  /**
7103
   * @see https://url.spec.whatwg.org/#query-state
7104
   */
7105
  QUERY,
7106
7107
  /**
7108
   * @see https://url.spec.whatwg.org/#path-state
7109
   */
7110
  PATH,
7111
7112
  /**
7113
   * @see https://url.spec.whatwg.org/#path-start-state
7114
   */
7115
  PATH_START,
7116
7117
  /**
7118
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
7119
   */
7120
  OPAQUE_PATH,
7121
7122
  /**
7123
   * @see https://url.spec.whatwg.org/#port-state
7124
   */
7125
  PORT,
7126
};
7127
7128
/**
7129
 * Converts a parser state to its string name for debugging.
7130
 * @param s The state to convert.
7131
 * @return A string representation of the state.
7132
 */
7133
ada_warn_unused std::string to_string(ada::state s);
7134
7135
}  // namespace ada
7136
7137
#endif  // ADA_STATE_H
7138
/* end file include/ada/state.h */
7139
/* begin file include/ada/unicode.h */
7140
/**
7141
 * @file unicode.h
7142
 * @brief Definitions for all unicode specific functions.
7143
 */
7144
#ifndef ADA_UNICODE_H
7145
#define ADA_UNICODE_H
7146
7147
7148
#include <cstddef>
7149
#include <cstdint>
7150
#include <optional>
7151
#include <string>
7152
#include <string_view>
7153
7154
/**
7155
 * Unicode operations. These functions are not part of our public API and may
7156
 * change at any time.
7157
 *
7158
 * @private
7159
 * @namespace ada::unicode
7160
 * @brief Includes the definitions for unicode operations
7161
 */
7162
namespace ada::unicode {
7163
7164
/**
7165
 * @private
7166
 * We receive a UTF-8 string representing a domain name.
7167
 * If the string is percent encoded, we apply percent decoding.
7168
 *
7169
 * Given a domain, we need to identify its labels.
7170
 * They are separated by label-separators:
7171
 *
7172
 * U+002E (.) FULL STOP
7173
 * U+FF0E FULLWIDTH FULL STOP
7174
 * U+3002 IDEOGRAPHIC FULL STOP
7175
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
7176
 *
7177
 * They are all mapped to U+002E.
7178
 *
7179
 * We process each label into a string that should not exceed 63 octets.
7180
 * If the string is already punycode (starts with "xn--"), then we must
7181
 * scan it to look for unallowed code points.
7182
 * Otherwise, if the string is not pure ASCII, we need to transcode it
7183
 * to punycode by following RFC 3454 which requires us to
7184
 * - Map characters  (see section 3),
7185
 * - Normalize (see section 4),
7186
 * - Reject forbidden characters,
7187
 * - Check for right-to-left characters and if so, check all requirements (see
7188
 * section 6),
7189
 * - Optionally reject based on unassigned code points (section 7).
7190
 *
7191
 * The Unicode standard provides a table of code points with a mapping, a list
7192
 * of forbidden code points and so forth. This table is subject to change and
7193
 * will vary based on the implementation. For Unicode 15, the table is at
7194
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
7195
 * If you use ICU, they parse this table and map it to code using a Python
7196
 * script.
7197
 *
7198
 * The resulting strings should not exceed 255 octets according to RFC 1035
7199
 * section 2.3.4. ICU checks for label size and domain size, but these errors
7200
 * are ignored.
7201
 *
7202
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
7203
 *
7204
 */
7205
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
7206
              size_t first_percent);
7207
7208
/**
7209
 * @private
7210
 * Checks if the input has tab or newline characters.
7211
 *
7212
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
7213
 * enough that compilers like GCC can 'autovectorize it'.
7214
 */
7215
ada_really_inline bool has_tabs_or_newline(
7216
    std::string_view user_input) noexcept;
7217
7218
/**
7219
 * @private
7220
 * Checks if the input is a forbidden host code point.
7221
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
7222
 */
7223
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
7224
7225
/**
7226
 * @private
7227
 * Checks if the input contains a forbidden domain code point.
7228
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7229
 */
7230
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
7231
    const char* input, size_t length) noexcept;
7232
7233
/**
7234
 * @private
7235
 * Checks if the input contains a forbidden domain code point in which case
7236
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
7237
 * then the second bit is set to 1.
7238
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7239
 */
7240
ada_really_inline constexpr uint8_t
7241
contains_forbidden_domain_code_point_or_upper(const char* input,
7242
                                              size_t length) noexcept;
7243
7244
/**
7245
 * @private
7246
 * Checks if the input is a forbidden domain code point.
7247
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7248
 */
7249
ada_really_inline constexpr bool is_forbidden_domain_code_point(
7250
    char c) noexcept;
7251
7252
/**
7253
 * @private
7254
 * Checks if the input is alphanumeric, '+', '-' or '.'
7255
 */
7256
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
7257
7258
/**
7259
 * @private
7260
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
7261
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
7262
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
7263
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
7264
 */
7265
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
7266
7267
/**
7268
 * @private
7269
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
7270
 * inclusive.
7271
 */
7272
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
7273
7274
/**
7275
 * @private
7276
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
7277
 * character.
7278
 */
7279
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
7280
7281
/**
7282
 * @private
7283
 * Checks if the input is a C0 control or space character.
7284
 *
7285
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
7286
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
7287
 * SEPARATOR ONE, inclusive.
7288
 */
7289
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
7290
7291
/**
7292
 * @private
7293
 * Checks if the input is a ASCII tab or newline character.
7294
 *
7295
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
7296
 */
7297
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
7298
7299
/**
7300
 * @private
7301
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
7302
 * match for ".%2e", "%2e.", or "%2e%2e".
7303
 */
7304
ada_really_inline constexpr bool is_double_dot_path_segment(
7305
    std::string_view input) noexcept;
7306
7307
/**
7308
 * @private
7309
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
7310
 * match for "%2e".
7311
 */
7312
ada_really_inline constexpr bool is_single_dot_path_segment(
7313
    std::string_view input) noexcept;
7314
7315
/**
7316
 * @private
7317
 * @details ipv4 character might contain 0-9 or a-f character ranges.
7318
 */
7319
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
7320
7321
/**
7322
 * @private
7323
 * @details Convert hex to binary. Caller is responsible to ensure that
7324
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
7325
 */
7326
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
7327
7328
/**
7329
 * @private
7330
 * first_percent should be  = input.find('%')
7331
 *
7332
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
7333
 * @author Node.js
7334
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
7335
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
7336
 */
7337
std::string percent_decode(std::string_view input, size_t first_percent);
7338
7339
/**
7340
 * Decode an application/x-www-form-urlencoded component: map '+' to space,
7341
 * then percent-decode. Single allocation; no intermediate string.
7342
 *
7343
 * @param input A form-urlencoded component (key or value).
7344
 * @return The decoded string.
7345
 * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
7346
 */
7347
std::string form_urlencoded_decode(std::string_view input);
7348
7349
/**
7350
 * @private
7351
 * Returns a percent-encoding string whether percent encoding was needed or not.
7352
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7353
 */
7354
std::string percent_encode(std::string_view input,
7355
                           const uint8_t character_set[]);
7356
/**
7357
 * @private
7358
 * Returns a percent-encoded string version of input, while starting the percent
7359
 * encoding at the provided index.
7360
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7361
 */
7362
std::string percent_encode(std::string_view input,
7363
                           const uint8_t character_set[], size_t index);
7364
/**
7365
 * @private
7366
 * Returns true if percent encoding was needed, in which case, we store
7367
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
7368
 * true, the content is appended to 'out'.
7369
 * If percent encoding is not needed, out is left unchanged.
7370
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7371
 */
7372
template <bool append>
7373
bool percent_encode(std::string_view input, const uint8_t character_set[],
7374
                    std::string& out);
7375
/**
7376
 * @private
7377
 * Returns the index at which percent encoding should start, or (equivalently),
7378
 * the length of the prefix that does not require percent encoding.
7379
 */
7380
ada_really_inline size_t percent_encode_index(std::string_view input,
7381
                                              const uint8_t character_set[]);
7382
/**
7383
 * @private
7384
 * Lowers the string in-place, assuming that the content is ASCII.
7385
 * Return true if the content was ASCII.
7386
 */
7387
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
7388
}  // namespace ada::unicode
7389
7390
#endif  // ADA_UNICODE_H
7391
/* end file include/ada/unicode.h */
7392
/* begin file include/ada/url_base-inl.h */
7393
/**
7394
 * @file url_base-inl.h
7395
 * @brief Inline functions for url base
7396
 */
7397
#ifndef ADA_URL_BASE_INL_H
7398
#define ADA_URL_BASE_INL_H
7399
7400
7401
#include <cstdint>
7402
#include <string>
7403
#if ADA_REGULAR_VISUAL_STUDIO
7404
#include <intrin.h>
7405
#endif  // ADA_REGULAR_VISUAL_STUDIO
7406
7407
namespace ada {
7408
7409
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
7410
46.1k
    const noexcept {
7411
46.1k
  return type != ada::scheme::NOT_SPECIAL;
7412
46.1k
}
7413
7414
0
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
7415
0
  return ada::scheme::get_special_port(type);
7416
0
}
7417
7418
[[nodiscard]] ada_really_inline uint16_t
7419
93
url_base::scheme_default_port() const noexcept {
7420
93
  return scheme::get_special_port(type);
7421
93
}
7422
7423
}  // namespace ada
7424
7425
#endif  // ADA_URL_BASE_INL_H
7426
/* end file include/ada/url_base-inl.h */
7427
/* begin file include/ada/url-inl.h */
7428
/**
7429
 * @file url-inl.h
7430
 * @brief Definitions for the URL
7431
 */
7432
#ifndef ADA_URL_INL_H
7433
#define ADA_URL_INL_H
7434
7435
7436
#include <charconv>
7437
#include <cstdint>
7438
#include <cstring>
7439
#include <optional>
7440
#include <ostream>
7441
#include <string>
7442
#include <string_view>
7443
#include <system_error>
7444
#include <utility>
7445
#if ADA_REGULAR_VISUAL_STUDIO
7446
#include <intrin.h>
7447
#endif  // ADA_REGULAR_VISUAL_STUDIO
7448
7449
namespace ada {
7450
0
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7451
0
  return !username.empty() || !password.empty();
7452
0
}
7453
0
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7454
0
  return port.has_value();
7455
0
}
7456
0
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7457
0
  return !host.has_value() || host->empty() || type == ada::scheme::type::FILE;
7458
0
}
7459
0
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7460
0
  if (!host.has_value()) {
7461
0
    return false;
7462
0
  }
7463
0
  return host->empty();
7464
0
}
7465
0
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7466
0
  return host.has_value();
7467
0
}
7468
0
inline std::ostream& operator<<(std::ostream& out, const ada::url& u) {
7469
0
  return out << u.to_string();
7470
0
}
7471
7472
0
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7473
0
  return path.size();
7474
0
}
7475
7476
0
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7477
0
  return path;
7478
0
}
7479
7480
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7481
0
    const {
7482
0
  url_components out{};
7483
0
7484
0
  // protocol ends with ':'. for example: "https:"
7485
0
  out.protocol_end = uint32_t(get_protocol().size());
7486
0
7487
0
  // Trailing index is always the next character of the current one.
7488
0
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7489
0
  size_t running_index = out.protocol_end;
7490
0
7491
0
  if (host.has_value()) {
7492
0
    // 2 characters for "//" and 1 character for starting index
7493
0
    out.host_start = out.protocol_end + 2;
7494
0
7495
0
    if (has_credentials()) {
7496
0
      out.username_end = uint32_t(out.host_start + username.size());
7497
0
7498
0
      out.host_start += uint32_t(username.size());
7499
0
7500
0
      if (!password.empty()) {
7501
0
        out.host_start += uint32_t(password.size() + 1);
7502
0
      }
7503
0
7504
0
      // host_start is the '@' when there are credentials, exactly as
7505
0
      // url_aggregator reports it, so the host itself begins one byte later.
7506
0
      out.host_end = uint32_t(out.host_start + 1 + host->size());
7507
0
    } else {
7508
0
      out.username_end = out.host_start;
7509
0
7510
0
      // Host does not start with "@" if it does not include credentials.
7511
0
      out.host_end = uint32_t(out.host_start + host->size());
7512
0
    }
7513
0
7514
0
    // host_end is one past the host, so it is already the next index.
7515
0
    running_index = out.host_end;
7516
0
  } else {
7517
0
    // Update host start and end date to the same index, since it does not
7518
0
    // exist.
7519
0
    out.host_start = out.protocol_end;
7520
0
    out.host_end = out.host_start;
7521
0
7522
0
    if (!has_opaque_path && path.starts_with("//")) {
7523
0
      // If url's host is null, url does not have an opaque path, url's path's
7524
0
      // size is greater than 1, and url's path[0] is the empty string, then
7525
0
      // append U+002F (/) followed by U+002E (.) to output.
7526
0
      running_index = out.protocol_end + 2;
7527
0
    } else {
7528
0
      running_index = out.protocol_end;
7529
0
    }
7530
0
  }
7531
0
7532
0
  if (port.has_value()) {
7533
0
    out.port = *port;
7534
0
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7535
0
  }
7536
0
7537
0
  out.pathname_start = uint32_t(running_index);
7538
0
7539
0
  running_index += path.size();
7540
0
7541
0
  if (query.has_value()) {
7542
0
    out.search_start = uint32_t(running_index);
7543
0
    running_index += get_search().size();
7544
0
    if (get_search().empty()) {
7545
0
      running_index++;
7546
0
    }
7547
0
  }
7548
0
7549
0
  if (hash.has_value()) {
7550
0
    out.hash_start = uint32_t(running_index);
7551
0
  }
7552
0
7553
0
  return out;
7554
0
}
7555
7556
0
inline void url::update_base_hostname(std::string_view input) { host = input; }
7557
7558
0
inline void url::update_unencoded_base_hash(std::string_view input) {
7559
  // We do the percent encoding
7560
0
  hash = unicode::percent_encode(input,
7561
0
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7562
0
}
7563
7564
inline void url::update_base_search(std::string_view input,
7565
0
                                    const uint8_t query_percent_encode_set[]) {
7566
0
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7567
0
}
7568
7569
0
inline void url::update_base_search(std::optional<std::string>&& input) {
7570
0
  query = std::move(input);
7571
0
}
7572
7573
0
inline void url::update_base_pathname(const std::string_view input) {
7574
0
  path = input;
7575
0
}
7576
7577
0
inline void url::update_base_username(const std::string_view input) {
7578
0
  username = input;
7579
0
}
7580
7581
0
inline void url::update_base_password(const std::string_view input) {
7582
0
  password = input;
7583
0
}
7584
7585
0
inline void url::update_base_port(std::optional<uint16_t> input) {
7586
0
  port = input;
7587
0
}
7588
7589
0
constexpr void url::clear_pathname() { path.clear(); }
7590
7591
0
constexpr void url::clear_search() { query = std::nullopt; }
7592
7593
0
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7594
0
  return hash.has_value();
7595
0
}
7596
7597
0
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7598
0
  return query.has_value();
7599
0
}
7600
7601
0
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7602
7603
0
inline void url::set_scheme(std::string&& new_scheme) noexcept {
7604
0
  type = ada::scheme::get_scheme_type(new_scheme);
7605
  // We only move the 'scheme' if it is non-special.
7606
0
  if (!is_special()) {
7607
0
    non_special_scheme = std::move(new_scheme);
7608
0
  }
7609
0
}
7610
7611
0
constexpr void url::copy_scheme(ada::url&& u) {
7612
0
  non_special_scheme = u.non_special_scheme;
7613
0
  type = u.type;
7614
0
}
7615
7616
0
constexpr void url::copy_scheme(const ada::url& u) {
7617
0
  non_special_scheme = u.non_special_scheme;
7618
0
  type = u.type;
7619
0
}
7620
7621
0
[[nodiscard]] ada_really_inline std::string url::get_href() const {
7622
0
  if (is_special() && host.has_value() && username.empty() &&
7623
0
      password.empty() && !port.has_value()) [[likely]] {
7624
0
    const std::string_view scheme = ada::scheme::details::is_special_list[type];
7625
0
    const size_t host_size = host->size();
7626
0
    const size_t path_size = path.size();
7627
0
    const size_t query_size = query.has_value() ? query->size() : 0;
7628
0
    const size_t hash_size = hash.has_value() ? hash->size() : 0;
7629
0
    const size_t total = scheme.size() + 3 + host_size + path_size +
7630
0
                         (query.has_value() ? query_size + 1 : 0) +
7631
0
                         (hash.has_value() ? hash_size + 1 : 0);
7632
0
    std::string output(total, '\0');
7633
0
    char* p = output.data();
7634
0
    std::memcpy(p, scheme.data(), scheme.size());
7635
0
    p += scheme.size();
7636
0
    p[0] = ':';
7637
0
    p[1] = '/';
7638
0
    p[2] = '/';
7639
0
    p += 3;
7640
0
    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7641
0
    std::memcpy(p, host->data(), host_size);
7642
0
    p += host_size;
7643
0
    std::memcpy(p, path.data(), path_size);
7644
0
    p += path_size;
7645
0
    if (query.has_value()) {
7646
0
      *p++ = '?';
7647
0
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7648
0
      std::memcpy(p, query->data(), query_size);
7649
0
      p += query_size;
7650
0
    }
7651
0
    if (hash.has_value()) {
7652
0
      *p++ = '#';
7653
0
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7654
0
      std::memcpy(p, hash->data(), hash_size);
7655
0
    }
7656
0
    return output;
7657
0
  }
7658
0
7659
0
  std::string output;
7660
0
  output.reserve(get_href_size());
7661
0
7662
0
  if (is_special()) {
7663
0
    output.append(ada::scheme::details::is_special_list[type]);
7664
0
    output += ':';
7665
0
  } else {
7666
0
    output.append(non_special_scheme);
7667
0
    output += ':';
7668
0
  }
7669
0
7670
0
  if (host.has_value()) {
7671
0
    output += '/';
7672
0
    output += '/';
7673
0
    if (has_credentials()) {
7674
0
      output.append(username);
7675
0
      if (!password.empty()) {
7676
0
        output += ':';
7677
0
        output.append(password);
7678
0
      }
7679
0
      output += '@';
7680
0
    }
7681
0
    output.append(*host);
7682
0
    if (port.has_value()) {
7683
0
      output += ':';
7684
0
      char port_buf[5];
7685
0
      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
7686
0
      (void)ec;
7687
0
      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
7688
0
    }
7689
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7690
0
    // If url's host is null, url does not have an opaque path, url's path's
7691
0
    // size is greater than 1, and url's path[0] is the empty string, then
7692
0
    // append U+002F (/) followed by U+002E (.) to output.
7693
0
    output += '/';
7694
0
    output += '.';
7695
0
  }
7696
0
  output.append(path);
7697
0
  if (query.has_value()) {
7698
0
    output += '?';
7699
0
    output.append(*query);
7700
0
  }
7701
0
  if (hash.has_value()) {
7702
0
    output += '#';
7703
0
    output.append(*hash);
7704
0
  }
7705
0
  return output;
7706
0
}
7707
7708
0
[[nodiscard]] inline size_t url::get_href_size() const noexcept {
7709
0
  size_t size = 0;
7710
0
  if (is_special()) {
7711
0
    size += ada::scheme::details::is_special_list[type].size() + 1;
7712
0
  } else {
7713
0
    size += non_special_scheme.size() + 1;
7714
0
  }
7715
0
  if (host.has_value()) {
7716
0
    size += host->size();
7717
0
    size += 2;
7718
0
    if (has_credentials()) {
7719
0
      size += username.size();
7720
0
      if (!password.empty()) {
7721
0
        size += 1 + password.size();
7722
0
      }
7723
0
      size += 1;
7724
0
    }
7725
0
    if (port.has_value()) {
7726
0
      size += 1;
7727
0
      uint16_t p = *port;
7728
0
      size += (p >= 10000)  ? 5
7729
0
              : (p >= 1000) ? 4
7730
0
              : (p >= 100)  ? 3
7731
0
              : (p >= 10)   ? 2
7732
0
                            : 1;
7733
0
    }
7734
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7735
0
    size += 2;
7736
0
  }
7737
0
  size += path.size();
7738
0
  if (query.has_value()) {
7739
0
    size += 1 + query->size();
7740
0
  }
7741
0
  if (hash.has_value()) {
7742
0
    size += 1 + hash->size();
7743
0
  }
7744
0
  return size;
7745
0
}
7746
7747
ada_really_inline size_t url::parse_port(std::string_view view,
7748
0
                                         bool check_trailing_content) noexcept {
7749
0
  ada_log("parse_port('", view, "') ", view.size());
7750
0
  if (!view.empty() && view[0] == '-') {
7751
0
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7752
0
    is_valid = false;
7753
0
    return 0;
7754
0
  }
7755
0
  uint16_t parsed_port{};
7756
0
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7757
0
  if (r.ec == std::errc::result_out_of_range) {
7758
0
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7759
0
    is_valid = false;
7760
0
    return 0;
7761
0
  }
7762
0
  ada_log("parse_port: ", parsed_port);
7763
0
  const auto consumed = size_t(r.ptr - view.data());
7764
0
  ada_log("parse_port: consumed ", consumed);
7765
0
  if (check_trailing_content) {
7766
0
    is_valid &=
7767
0
        (consumed == view.size() || view[consumed] == '/' ||
7768
0
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7769
0
  }
7770
0
  ada_log("parse_port: is_valid = ", is_valid);
7771
0
  if (is_valid) {
7772
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7773
0
    auto default_port = scheme_default_port();
7774
0
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7775
0
                         (default_port != parsed_port);
7776
0
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7777
0
                                                  : std::nullopt;
7778
0
  }
7779
0
  return consumed;
7780
0
}
7781
7782
}  // namespace ada
7783
7784
#endif  // ADA_URL_H
7785
/* end file include/ada/url-inl.h */
7786
/* begin file include/ada/url_components-inl.h */
7787
/**
7788
 * @file url_components.h
7789
 * @brief Declaration for the URL Components
7790
 */
7791
#ifndef ADA_URL_COMPONENTS_INL_H
7792
#define ADA_URL_COMPONENTS_INL_H
7793
7794
7795
#include <cstdint>
7796
7797
namespace ada {
7798
7799
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7800
2.52k
    const noexcept {
7801
  /**
7802
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7803
   *       |     |    |          | ^^^^|       |   |
7804
   *       |     |    |          | |   |       |   `----- hash_start
7805
   *       |     |    |          | |   |       `--------- search_start
7806
   *       |     |    |          | |   `----------------- pathname_start
7807
   *       |     |    |          | `--------------------- port
7808
   *       |     |    |          `----------------------- host_end
7809
   *       |     |    `---------------------------------- host_start
7810
   *       |     `--------------------------------------- username_end
7811
   *       `--------------------------------------------- protocol_end
7812
   */
7813
  // These conditions can be made more strict.
7814
2.52k
  if (protocol_end == url_components::omitted) {
7815
0
    return false;
7816
0
  }
7817
2.52k
  uint32_t index = protocol_end;
7818
7819
2.52k
  if (username_end == url_components::omitted) {
7820
0
    return false;
7821
0
  }
7822
2.52k
  if (username_end < index) {
7823
0
    return false;
7824
0
  }
7825
2.52k
  index = username_end;
7826
7827
2.52k
  if (host_start == url_components::omitted) {
7828
0
    return false;
7829
0
  }
7830
2.52k
  if (host_start < index) {
7831
0
    return false;
7832
0
  }
7833
2.52k
  index = host_start;
7834
7835
2.52k
  if (port != url_components::omitted) {
7836
175
    if (port > 0xffff) {
7837
0
      return false;
7838
0
    }
7839
175
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7840
175
    if (index + port_length < index) {
7841
0
      return false;
7842
0
    }
7843
175
    index += port_length;
7844
175
  }
7845
7846
2.52k
  if (pathname_start == url_components::omitted) {
7847
0
    return false;
7848
0
  }
7849
2.52k
  if (pathname_start < index) {
7850
0
    return false;
7851
0
  }
7852
2.52k
  index = pathname_start;
7853
7854
2.52k
  if (search_start != url_components::omitted) {
7855
756
    if (search_start < index) {
7856
0
      return false;
7857
0
    }
7858
756
    index = search_start;
7859
756
  }
7860
7861
2.52k
  if (hash_start != url_components::omitted) {
7862
382
    if (hash_start < index) {
7863
0
      return false;
7864
0
    }
7865
382
  }
7866
7867
2.52k
  return true;
7868
2.52k
}
7869
7870
}  // namespace ada
7871
#endif
7872
/* end file include/ada/url_components-inl.h */
7873
/* begin file include/ada/url_aggregator.h */
7874
/**
7875
 * @file url_aggregator.h
7876
 * @brief Declaration for the `ada::url_aggregator` class.
7877
 *
7878
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7879
 * URL using a single buffer with component offsets. This is the default and
7880
 * most memory-efficient URL representation in Ada.
7881
 *
7882
 * @see url.h for an alternative representation using separate strings
7883
 */
7884
#ifndef ADA_URL_AGGREGATOR_H
7885
#define ADA_URL_AGGREGATOR_H
7886
7887
#include <cstddef>
7888
#include <cstdint>
7889
#include <ostream>
7890
#include <string>
7891
#include <string_view>
7892
#include <variant>
7893
7894
7895
namespace ada {
7896
7897
namespace parser {}
7898
7899
/**
7900
 * @brief Memory-efficient URL representation using a single buffer.
7901
 *
7902
 * The `url_aggregator` stores the entire normalized URL in a single string
7903
 * buffer and tracks component boundaries using offsets. This design minimizes
7904
 * memory allocations and is ideal for read-mostly access patterns.
7905
 *
7906
 * Getter methods return `std::string_view` pointing into the internal buffer.
7907
 * These views are lightweight (no allocation) but become invalid if the
7908
 * url_aggregator is modified or destroyed.
7909
 *
7910
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7911
 * when any setter is called. Do not use a getter's result as input to a
7912
 * setter on the same object without copying first.
7913
 *
7914
 * @note This is the default URL type returned by `ada::parse()`.
7915
 *
7916
 * @see url For an alternative using separate std::string instances
7917
 */
7918
struct url_aggregator : url_base {
7919
13.4k
  url_aggregator() = default;
7920
0
  url_aggregator(const url_aggregator& u) = default;
7921
2.52k
  url_aggregator(url_aggregator&& u) noexcept = default;
7922
0
  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
7923
0
  url_aggregator& operator=(const url_aggregator& u) = default;
7924
15.9k
  ~url_aggregator() override = default;
7925
7926
  /**
7927
   * The setter functions follow the steps defined in the URL Standard.
7928
   *
7929
   * The url_aggregator has a single buffer that contains the entire normalized
7930
   * URL. The various components are represented as offsets into that buffer.
7931
   * When you call get_pathname(), for example, you get a std::string_view that
7932
   * points into that buffer. If the url_aggregator is modified, the buffer may
7933
   * be reallocated, and the std::string_view you obtained earlier may become
7934
   * invalid. In particular, this implies that you cannot modify the URL using
7935
   * a setter function with a std::string_view that points into the
7936
   * url_aggregator E.g., the following is incorrect:
7937
   * url->set_hostname(url->get_pathname()).
7938
   * You must first copy the pathname to a separate string.
7939
   * std::string pathname(url->get_pathname());
7940
   * url->set_hostname(pathname);
7941
   *
7942
   * The caller is responsible for ensuring that the url_aggregator is not
7943
   * modified while any std::string_view obtained from it is in use.
7944
   */
7945
  bool set_href(std::string_view input);
7946
  bool set_host(std::string_view input);
7947
  bool set_hostname(std::string_view input);
7948
  bool set_protocol(std::string_view input);
7949
  bool set_username(std::string_view input);
7950
  bool set_password(std::string_view input);
7951
  bool set_port(std::string_view input);
7952
  bool set_pathname(std::string_view input);
7953
  void set_search(std::string_view input);
7954
  void set_hash(std::string_view input);
7955
7956
  /**
7957
   * Validates whether the hostname is a valid domain according to RFC 1034.
7958
   * @return `true` if the domain is valid, `false` otherwise.
7959
   */
7960
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7961
7962
  /**
7963
   * Returns the URL's origin (scheme + host + port for special URLs).
7964
   * @return A newly allocated string containing the serialized origin.
7965
   * @see https://url.spec.whatwg.org/#concept-url-origin
7966
   */
7967
  [[nodiscard]] std::string get_origin() const override;
7968
7969
  /**
7970
   * Returns the full serialized URL (the href) as a string_view.
7971
   * Does not allocate memory. The returned view becomes invalid if this
7972
   * url_aggregator is modified or destroyed.
7973
   * @return A string_view into the internal buffer.
7974
   * @see https://url.spec.whatwg.org/#dom-url-href
7975
   */
7976
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7977
      ada_lifetime_bound;
7978
7979
  /**
7980
   * Returns the byte length of the serialized URL without allocating a string.
7981
   * @return Size of the href in bytes.
7982
   */
7983
  [[nodiscard]] constexpr size_t get_href_size() const noexcept;
7984
7985
  /**
7986
   * Returns the URL's username component.
7987
   * Does not allocate memory. The returned view becomes invalid if this
7988
   * url_aggregator is modified or destroyed.
7989
   * @return A string_view of the username.
7990
   * @see https://url.spec.whatwg.org/#dom-url-username
7991
   */
7992
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
7993
7994
  /**
7995
   * Returns the URL's password component.
7996
   * Does not allocate memory. The returned view becomes invalid if this
7997
   * url_aggregator is modified or destroyed.
7998
   * @return A string_view of the password.
7999
   * @see https://url.spec.whatwg.org/#dom-url-password
8000
   */
8001
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
8002
8003
  /**
8004
   * Returns the URL's port as a string (e.g., "8080").
8005
   * Does not allocate memory. Returns empty view if no port is set.
8006
   * The returned view becomes invalid if this url_aggregator is modified.
8007
   * @return A string_view of the port.
8008
   * @see https://url.spec.whatwg.org/#dom-url-port
8009
   */
8010
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
8011
8012
  /**
8013
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
8014
   * Does not allocate memory. Returns empty view if no fragment is set.
8015
   * The returned view becomes invalid if this url_aggregator is modified.
8016
   * @return A string_view of the hash.
8017
   * @see https://url.spec.whatwg.org/#dom-url-hash
8018
   */
8019
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
8020
8021
  /**
8022
   * Returns the URL's host and port (e.g., "example.com:8080").
8023
   * Does not allocate memory. Returns empty view if no host is set.
8024
   * The returned view becomes invalid if this url_aggregator is modified.
8025
   * @return A string_view of host:port.
8026
   * @see https://url.spec.whatwg.org/#dom-url-host
8027
   */
8028
  [[nodiscard]] std::string_view get_host() const ada_lifetime_bound;
8029
8030
  /**
8031
   * Returns the URL's hostname (without port).
8032
   * Does not allocate memory. Returns empty view if no host is set.
8033
   * The returned view becomes invalid if this url_aggregator is modified.
8034
   * @return A string_view of the hostname.
8035
   * @see https://url.spec.whatwg.org/#dom-url-hostname
8036
   */
8037
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
8038
8039
  /**
8040
   * Returns the URL's path component.
8041
   * Does not allocate memory. The returned view becomes invalid if this
8042
   * url_aggregator is modified or destroyed.
8043
   * @return A string_view of the pathname.
8044
   * @see https://url.spec.whatwg.org/#dom-url-pathname
8045
   */
8046
  [[nodiscard]] constexpr std::string_view get_pathname() const
8047
      ada_lifetime_bound;
8048
8049
  /**
8050
   * Returns the byte length of the pathname without creating a string.
8051
   * @return Size of the pathname in bytes.
8052
   * @see https://url.spec.whatwg.org/#dom-url-pathname
8053
   */
8054
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
8055
8056
  /**
8057
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
8058
   * Does not allocate memory. Returns empty view if no query is set.
8059
   * The returned view becomes invalid if this url_aggregator is modified.
8060
   * @return A string_view of the search/query.
8061
   * @see https://url.spec.whatwg.org/#dom-url-search
8062
   */
8063
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
8064
8065
  /**
8066
   * Returns the URL's scheme followed by a colon (e.g., "https:").
8067
   * Does not allocate memory. The returned view becomes invalid if this
8068
   * url_aggregator is modified or destroyed.
8069
   * @return A string_view of the protocol.
8070
   * @see https://url.spec.whatwg.org/#dom-url-protocol
8071
   */
8072
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
8073
8074
  /**
8075
   * Checks if the URL has credentials (non-empty username or password).
8076
   * @return `true` if username or password is non-empty, `false` otherwise.
8077
   */
8078
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
8079
      const noexcept;
8080
8081
  /**
8082
   * Returns the URL component offsets for efficient serialization.
8083
   *
8084
   * The components represent byte offsets into the serialized URL:
8085
   * ```
8086
   * https://user:pass@example.com:1234/foo/bar?baz#quux
8087
   *       |     |    |          | ^^^^|       |   |
8088
   *       |     |    |          | |   |       |   `----- hash_start
8089
   *       |     |    |          | |   |       `--------- search_start
8090
   *       |     |    |          | |   `----------------- pathname_start
8091
   *       |     |    |          | `--------------------- port
8092
   *       |     |    |          `----------------------- host_end
8093
   *       |     |    `---------------------------------- host_start
8094
   *       |     `--------------------------------------- username_end
8095
   *       `--------------------------------------------- protocol_end
8096
   * ```
8097
   * @return A constant reference to the url_components struct.
8098
   * @see https://github.com/servo/rust-url
8099
   */
8100
  [[nodiscard]] ada_really_inline const url_components& get_components()
8101
      const noexcept;
8102
8103
  /**
8104
   * Returns a JSON string representation of this URL for debugging.
8105
   * @return A JSON-formatted string with all URL components.
8106
   */
8107
  [[nodiscard]] std::string to_string() const override;
8108
8109
  /**
8110
   * Returns a visual diagram showing component boundaries in the URL.
8111
   * Useful for debugging and understanding URL structure.
8112
   * @return A multi-line string diagram.
8113
   */
8114
  [[nodiscard]] std::string to_diagram() const;
8115
8116
  /**
8117
   * Validates internal consistency of component offsets (for debugging).
8118
   * @return `true` if offsets are consistent, `false` if corrupted.
8119
   */
8120
  [[nodiscard]] constexpr bool validate() const noexcept;
8121
8122
  /**
8123
   * Checks if the URL has an empty hostname (host is set but empty string).
8124
   * @return `true` if host exists but is empty, `false` otherwise.
8125
   */
8126
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
8127
8128
  /**
8129
   * Checks if the URL has a hostname (including empty hostnames).
8130
   * @return `true` if host is present, `false` otherwise.
8131
   */
8132
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
8133
8134
  /**
8135
   * Checks if the URL has a non-empty username.
8136
   * @return `true` if username is non-empty, `false` otherwise.
8137
   */
8138
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
8139
8140
  /**
8141
   * Checks if the URL has a non-empty password.
8142
   * @return `true` if password is non-empty, `false` otherwise.
8143
   */
8144
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
8145
8146
  /**
8147
   * Checks if the URL has a non-default port explicitly specified.
8148
   * @return `true` if a port is present, `false` otherwise.
8149
   */
8150
  [[nodiscard]] constexpr bool has_port() const noexcept;
8151
8152
  /**
8153
   * Checks if the URL has a password component (may be empty).
8154
   * @return `true` if password is present, `false` otherwise.
8155
   */
8156
  [[nodiscard]] constexpr bool has_password() const noexcept;
8157
8158
  /**
8159
   * Checks if the URL has a fragment/hash component.
8160
   * @return `true` if hash is present, `false` otherwise.
8161
   */
8162
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
8163
8164
  /**
8165
   * Checks if the URL has a query/search component.
8166
   * @return `true` if query is present, `false` otherwise.
8167
   */
8168
  [[nodiscard]] constexpr bool has_search() const noexcept override;
8169
8170
  /**
8171
   * Removes the port from the URL.
8172
   */
8173
  inline void clear_port();
8174
8175
  /**
8176
   * Removes the hash/fragment from the URL.
8177
   */
8178
  inline void clear_hash();
8179
8180
  /**
8181
   * Removes the query/search string from the URL.
8182
   */
8183
  inline void clear_search() override;
8184
8185
 private:
8186
  // helper methods
8187
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
8188
      url_aggregator& url);
8189
  // parse_url methods
8190
  friend url_aggregator parser::parse_url<url_aggregator>(
8191
      std::string_view, const url_aggregator*);
8192
8193
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
8194
      std::string_view, const url_aggregator*);
8195
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
8196
      std::string_view, const url_aggregator*);
8197
  template <class result_type>
8198
  friend bool parser::try_parse_simple_absolute(std::string_view, result_type&);
8199
  template <class result_type>
8200
  friend bool parser::finish_simple_absolute_with_port(std::string_view,
8201
                                                       result_type&,
8202
                                                       ada::scheme::type,
8203
                                                       uint32_t, size_t, size_t,
8204
                                                       size_t, bool);
8205
  template <class result_type>
8206
  friend bool parser::try_parse_simple_relative(std::string_view,
8207
                                                const result_type&,
8208
                                                result_type&);
8209
8210
#if ADA_INCLUDE_URL_PATTERN
8211
  // url_pattern methods
8212
  template <url_pattern_regex::regex_concept regex_provider>
8213
  friend tl::expected<url_pattern<regex_provider>, errors>
8214
  parse_url_pattern_impl(
8215
      std::variant<std::string_view, url_pattern_init>&& input,
8216
      const std::string_view* base_url, const url_pattern_options* options);
8217
#endif  // ADA_INCLUDE_URL_PATTERN
8218
8219
  // components is declared before buffer so that the offset fields land
8220
  // close to url_base in memory, improving cache locality for getter calls.
8221
  // Note: exact cache-line placement is implementation- and platform-dependent.
8222
  url_components components{};
8223
  std::string buffer{};
8224
8225
  /**
8226
   * Returns true if neither the search, nor the hash nor the pathname
8227
   * have been set.
8228
   * @return true if the buffer is ready to receive the path.
8229
   */
8230
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
8231
8232
  inline void add_authority_slashes_if_needed();
8233
8234
  /**
8235
   * To optimize performance, you may indicate how much memory to allocate
8236
   * within this instance.
8237
   */
8238
  constexpr void reserve(uint32_t capacity);
8239
8240
  ada_really_inline size_t parse_port(std::string_view view,
8241
                                      bool check_trailing_content) override;
8242
8243
0
  ada_really_inline size_t parse_port(std::string_view view) override {
8244
0
    return this->parse_port(view, false);
8245
0
  }
8246
8247
  /**
8248
   * Return true on success. The 'in_place' parameter indicates whether the
8249
   * the string_view input is pointing in the buffer. When in_place is false,
8250
   * we must nearly always update the buffer.
8251
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
8252
   */
8253
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
8254
8255
  /**
8256
   * Return true on success.
8257
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
8258
   */
8259
  [[nodiscard]] bool parse_ipv6(std::string_view input);
8260
8261
  /**
8262
   * Return true on success.
8263
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
8264
   */
8265
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
8266
8267
  ada_really_inline void parse_path(std::string_view input);
8268
8269
  /**
8270
   * A URL cannot have a username/password/port if its host is null or the empty
8271
   * string, or its scheme is "file".
8272
   */
8273
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
8274
8275
  /**
8276
   * @private
8277
   * Several setters restore a saved copy of the URL when the mutation pushes
8278
   * the buffer past get_max_input_length(). A setter grows the buffer by at
8279
   * most input_len * 3 (worst-case percent-encoding) plus a small constant for
8280
   * delimiters, so this returns false when that upper bound stays within the
8281
   * limit. It does not account for parse-failure rollbacks, which are
8282
   * unrelated to the length limit.
8283
   */
8284
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
8285
8286
  template <bool override_hostname = false>
8287
  bool set_host_or_hostname(std::string_view input);
8288
8289
  ada_really_inline bool parse_host(std::string_view input);
8290
8291
  inline void update_base_authority(std::string_view base_buffer,
8292
                                    const url_components& base);
8293
  inline void update_unencoded_base_hash(std::string_view input);
8294
  inline void update_base_hostname(std::string_view input);
8295
  inline void update_base_search(std::string_view input);
8296
  inline void update_base_search(std::string_view input,
8297
                                 const uint8_t* query_percent_encode_set);
8298
  inline void update_base_pathname(std::string_view input);
8299
  inline void update_base_username(std::string_view input);
8300
  inline void append_base_username(std::string_view input);
8301
  inline void update_base_password(std::string_view input);
8302
  inline void append_base_password(std::string_view input);
8303
  inline void update_base_port(uint32_t input);
8304
  inline void append_base_pathname(std::string_view input);
8305
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
8306
  constexpr void clear_hostname();
8307
  constexpr void clear_password();
8308
  constexpr void clear_pathname() override;
8309
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
8310
  void delete_dash_dot();
8311
  inline void consume_prepared_path(std::string_view input);
8312
  template <bool has_state_override = false>
8313
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
8314
      std::string_view input);
8315
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
8316
                                                std::string_view input);
8317
  [[nodiscard]] constexpr bool has_authority() const noexcept;
8318
  constexpr void set_protocol_as_file();
8319
  inline void set_scheme(std::string_view new_scheme);
8320
  /**
8321
   * Fast function to set the scheme from a view with a colon in the
8322
   * buffer, does not change type.
8323
   */
8324
  inline void set_scheme_from_view_with_colon(
8325
      std::string_view new_scheme_with_colon);
8326
  inline void copy_scheme(const url_aggregator& u);
8327
8328
  inline void update_host_to_base_host(const std::string_view input);
8329
8330
};  // url_aggregator
8331
8332
inline std::ostream& operator<<(std::ostream& out, const url& u);
8333
}  // namespace ada
8334
8335
#endif
8336
/* end file include/ada/url_aggregator.h */
8337
/* begin file include/ada/url_aggregator-inl.h */
8338
/**
8339
 * @file url_aggregator-inl.h
8340
 * @brief Inline functions for url aggregator
8341
 */
8342
#ifndef ADA_URL_AGGREGATOR_INL_H
8343
#define ADA_URL_AGGREGATOR_INL_H
8344
8345
/* begin file include/ada/unicode-inl.h */
8346
/**
8347
 * @file unicode-inl.h
8348
 * @brief Definitions for unicode operations.
8349
 */
8350
#ifndef ADA_UNICODE_INL_H
8351
#define ADA_UNICODE_INL_H
8352
8353
#include <cstddef>
8354
#include <cstdint>
8355
#include <cstring>
8356
#include <string_view>
8357
8358
/**
8359
 * Unicode operations. These functions are not part of our public API and may
8360
 * change at any time.
8361
 *
8362
 * private
8363
 * @namespace ada::unicode
8364
 * @brief Includes the declarations for unicode operations
8365
 */
8366
namespace ada::unicode {
8367
ada_really_inline size_t percent_encode_index(const std::string_view input,
8368
7.11k
                                              const uint8_t character_set[]) {
8369
7.11k
  const char* data = input.data();
8370
7.11k
  const size_t size = input.size();
8371
8372
  // Process 8 bytes at a time using unrolled loop
8373
7.11k
  size_t i = 0;
8374
7.50k
  for (; i + 8 <= size; i += 8) {
8375
589
    unsigned char chunk[8];
8376
589
    std::memcpy(&chunk, data + i,
8377
589
                8);  // entices compiler to unconditionally process 8 characters
8378
8379
    // Check 8 characters at once
8380
4.05k
    for (size_t j = 0; j < 8; j++) {
8381
3.66k
      if (character_sets::bit_at(character_set, chunk[j])) {
8382
202
        return i + j;
8383
202
      }
8384
3.66k
    }
8385
589
  }
8386
8387
  // Handle remaining bytes
8388
7.28k
  for (; i < size; i++) {
8389
518
    if (character_sets::bit_at(character_set, data[i])) {
8390
152
      return i;
8391
152
    }
8392
518
  }
8393
8394
6.76k
  return size;
8395
6.91k
}
8396
}  // namespace ada::unicode
8397
8398
#endif  // ADA_UNICODE_INL_H
8399
/* end file include/ada/unicode-inl.h */
8400
8401
#include <charconv>
8402
#include <cstdint>
8403
#include <cstring>
8404
#include <ostream>
8405
#include <string>
8406
#include <string_view>
8407
#include <system_error>
8408
8409
namespace ada {
8410
8411
inline void url_aggregator::update_base_authority(
8412
0
    std::string_view base_buffer, const ada::url_components& base) {
8413
0
  std::string_view input = base_buffer.substr(
8414
0
      base.protocol_end, base.host_start - base.protocol_end);
8415
0
  ada_log("url_aggregator::update_base_authority ", input);
8416
8417
0
  bool input_starts_with_dash = input.starts_with("//");
8418
0
  uint32_t diff = components.host_start - components.protocol_end;
8419
8420
0
  buffer.erase(components.protocol_end,
8421
0
               components.host_start - components.protocol_end);
8422
0
  components.username_end = components.protocol_end;
8423
8424
0
  if (input_starts_with_dash) {
8425
0
    input.remove_prefix(2);
8426
0
    diff += 2;  // add "//"
8427
0
    buffer.insert(components.protocol_end, "//");
8428
0
    components.username_end += 2;
8429
0
  }
8430
8431
0
  size_t password_delimiter = input.find(':');
8432
8433
  // Check if input contains both username and password by checking the
8434
  // delimiter: ":" A typical input that contains authority would be "user:pass"
8435
0
  if (password_delimiter != std::string_view::npos) {
8436
    // Insert both username and password
8437
0
    std::string_view username = input.substr(0, password_delimiter);
8438
0
    std::string_view password = input.substr(password_delimiter + 1);
8439
8440
0
    buffer.insert(components.protocol_end + diff, username);
8441
0
    diff += uint32_t(username.size());
8442
0
    buffer.insert(components.protocol_end + diff, ":");
8443
0
    components.username_end = components.protocol_end + diff;
8444
0
    buffer.insert(components.protocol_end + diff + 1, password);
8445
0
    diff += uint32_t(password.size()) + 1;
8446
0
  } else if (!input.empty()) {
8447
    // Insert only username
8448
0
    buffer.insert(components.protocol_end + diff, input);
8449
0
    components.username_end =
8450
0
        components.protocol_end + diff + uint32_t(input.size());
8451
0
    diff += uint32_t(input.size());
8452
0
  }
8453
8454
0
  components.host_start += diff;
8455
8456
0
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
8457
0
    buffer.insert(components.host_start, "@");
8458
0
    diff++;
8459
0
  }
8460
0
  components.host_end += diff;
8461
0
  components.pathname_start += diff;
8462
0
  if (components.search_start != url_components::omitted) {
8463
0
    components.search_start += diff;
8464
0
  }
8465
0
  if (components.hash_start != url_components::omitted) {
8466
0
    components.hash_start += diff;
8467
0
  }
8468
0
}
8469
8470
262
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
8471
262
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
8472
262
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
8473
262
          " bytes] components.hash_start = ", components.hash_start);
8474
262
  ADA_ASSERT_TRUE(validate());
8475
262
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8476
262
  if (components.hash_start != url_components::omitted) {
8477
0
    buffer.resize(components.hash_start);
8478
0
  }
8479
262
  components.hash_start = uint32_t(buffer.size());
8480
262
  buffer += "#";
8481
262
  bool encoding_required = unicode::percent_encode<true>(
8482
262
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
8483
  // When encoding_required is false, then buffer is left unchanged, and percent
8484
  // encoding was not deemed required.
8485
262
  if (!encoding_required) {
8486
172
    buffer.append(input);
8487
172
  }
8488
262
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
8489
262
          buffer, "' [", buffer.size(), " bytes]");
8490
262
  ADA_ASSERT_TRUE(validate());
8491
262
}
8492
8493
ada_really_inline uint32_t url_aggregator::replace_and_resize(
8494
3.08k
    uint32_t start, uint32_t end, std::string_view input) {
8495
3.08k
  uint32_t current_length = end - start;
8496
3.08k
  uint32_t input_size = uint32_t(input.size());
8497
3.08k
  uint32_t new_difference = input_size - current_length;
8498
8499
3.08k
  if (current_length == 0) {
8500
2.99k
    buffer.insert(start, input);
8501
2.99k
  } else if (input_size == current_length) {
8502
8
    if (input_size != 0) {
8503
8
      std::memmove(buffer.data() + start, input.data(), input_size);
8504
8
    }
8505
81
  } else if (input_size < current_length) {
8506
0
    buffer.erase(start, current_length - input_size);
8507
0
    buffer.replace(start, input_size, input);
8508
81
  } else {
8509
81
    buffer.replace(start, current_length, input.substr(0, current_length));
8510
81
    buffer.insert(start + current_length, input.substr(current_length));
8511
81
  }
8512
8513
3.08k
  return new_difference;
8514
3.08k
}
8515
8516
1.71k
inline void url_aggregator::update_base_hostname(const std::string_view input) {
8517
1.71k
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
8518
1.71k
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
8519
1.71k
  ADA_ASSERT_TRUE(validate());
8520
1.71k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8521
8522
  // This next line is required for when parsing a URL like `foo://`
8523
1.71k
  add_authority_slashes_if_needed();
8524
8525
1.71k
  bool has_credentials = components.protocol_end + 2 < components.host_start;
8526
1.71k
  uint32_t new_difference =
8527
1.71k
      replace_and_resize(components.host_start, components.host_end, input);
8528
8529
1.71k
  if (has_credentials) {
8530
89
    buffer.insert(components.host_start, "@");
8531
89
    new_difference++;
8532
89
  }
8533
1.71k
  components.host_end += new_difference;
8534
1.71k
  components.pathname_start += new_difference;
8535
1.71k
  if (components.search_start != url_components::omitted) {
8536
0
    components.search_start += new_difference;
8537
0
  }
8538
1.71k
  if (components.hash_start != url_components::omitted) {
8539
0
    components.hash_start += new_difference;
8540
0
  }
8541
1.71k
  ADA_ASSERT_TRUE(validate());
8542
1.71k
}
8543
8544
[[nodiscard]] ada_really_inline uint32_t
8545
1.37k
url_aggregator::get_pathname_length() const noexcept {
8546
1.37k
  ada_log("url_aggregator::get_pathname_length");
8547
1.37k
  uint32_t ending_index = uint32_t(buffer.size());
8548
1.37k
  if (components.search_start != url_components::omitted) {
8549
0
    ending_index = components.search_start;
8550
1.37k
  } else if (components.hash_start != url_components::omitted) {
8551
0
    ending_index = components.hash_start;
8552
0
  }
8553
1.37k
  return ending_index - components.pathname_start;
8554
1.37k
}
8555
8556
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
8557
724
    const noexcept {
8558
724
  return buffer.size() == components.pathname_start;
8559
724
}
8560
8561
0
inline void url_aggregator::update_base_search(std::string_view input) {
8562
0
  ada_log("url_aggregator::update_base_search ", input);
8563
0
  ADA_ASSERT_TRUE(validate());
8564
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8565
0
  if (input.empty()) {
8566
0
    clear_search();
8567
0
    return;
8568
0
  }
8569
8570
0
  if (input[0] == '?') {
8571
0
    input.remove_prefix(1);
8572
0
  }
8573
8574
0
  if (components.hash_start == url_components::omitted) {
8575
0
    if (components.search_start == url_components::omitted) {
8576
0
      components.search_start = uint32_t(buffer.size());
8577
0
      buffer += "?";
8578
0
    } else {
8579
0
      buffer.resize(components.search_start + 1);
8580
0
    }
8581
8582
0
    buffer.append(input);
8583
0
  } else if (components.search_start != url_components::omitted) {
8584
0
    const uint32_t difference = replace_and_resize(
8585
0
        components.search_start + 1, components.hash_start, input);
8586
0
    components.hash_start += difference;
8587
0
  } else {
8588
0
    components.search_start = components.hash_start;
8589
0
    buffer.insert(components.search_start, input.size() + 1, '?');
8590
0
    if (!input.empty()) {
8591
0
      std::memmove(buffer.data() + components.search_start + 1, input.data(),
8592
0
                   input.size());
8593
0
    }
8594
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8595
0
  }
8596
8597
0
  ADA_ASSERT_TRUE(validate());
8598
0
}
8599
8600
inline void url_aggregator::update_base_search(
8601
629
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8602
629
  ada_log("url_aggregator::update_base_search ", input,
8603
629
          " with encoding parameter ", to_string(), "\n", to_diagram());
8604
629
  ADA_ASSERT_TRUE(validate());
8605
629
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8606
8607
629
  if (components.hash_start == url_components::omitted) {
8608
629
    if (components.search_start == url_components::omitted) {
8609
629
      components.search_start = uint32_t(buffer.size());
8610
629
      buffer += "?";
8611
629
    } else {
8612
0
      buffer.resize(components.search_start + 1);
8613
0
    }
8614
8615
629
    bool encoding_required =
8616
629
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8617
    // When encoding_required is false, then buffer is left unchanged, and
8618
    // percent encoding was not deemed required.
8619
629
    if (!encoding_required) {
8620
485
      buffer.append(input);
8621
485
    }
8622
629
  } else {
8623
0
    const size_t idx =
8624
0
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8625
0
    std::string encoded;
8626
0
    std::string_view replacement = input;
8627
0
    if (idx != input.size()) {
8628
0
      encoded =
8629
0
          ada::unicode::percent_encode(input, query_percent_encode_set, idx);
8630
0
      replacement = encoded;
8631
0
    }
8632
8633
0
    if (components.search_start != url_components::omitted) {
8634
0
      const uint32_t difference = replace_and_resize(
8635
0
          components.search_start + 1, components.hash_start, replacement);
8636
0
      components.hash_start += difference;
8637
0
    } else {
8638
0
      components.search_start = components.hash_start;
8639
0
      buffer.insert(components.search_start, replacement.size() + 1, '?');
8640
0
      if (!replacement.empty()) {
8641
0
        std::memmove(buffer.data() + components.search_start + 1,
8642
0
                     replacement.data(), replacement.size());
8643
0
      }
8644
0
      components.hash_start +=
8645
0
          uint32_t(replacement.size() + 1);  // Do not forget `?`
8646
0
    }
8647
0
  }
8648
8649
629
  ADA_ASSERT_TRUE(validate());
8650
629
}
8651
8652
1.37k
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8653
1.37k
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8654
1.37k
          " bytes] \n", to_diagram());
8655
1.37k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8656
1.37k
  ADA_ASSERT_TRUE(validate());
8657
8658
1.37k
  const bool begins_with_dashdash = input.starts_with("//");
8659
1.37k
  if (!begins_with_dashdash && has_dash_dot()) {
8660
    // We must delete the ./
8661
0
    delete_dash_dot();
8662
0
  }
8663
8664
1.37k
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8665
0
      !has_dash_dot()) {
8666
    // If url's host is null, url does not have an opaque path, url's path's
8667
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8668
    // output.
8669
0
    buffer.insert(components.pathname_start, "/.");
8670
0
    components.pathname_start += 2;
8671
0
    if (components.search_start != url_components::omitted) {
8672
0
      components.search_start += 2;
8673
0
    }
8674
0
    if (components.hash_start != url_components::omitted) {
8675
0
      components.hash_start += 2;
8676
0
    }
8677
0
  }
8678
8679
1.37k
  uint32_t difference = replace_and_resize(
8680
1.37k
      components.pathname_start,
8681
1.37k
      components.pathname_start + get_pathname_length(), input);
8682
1.37k
  if (components.search_start != url_components::omitted) {
8683
0
    components.search_start += difference;
8684
0
  }
8685
1.37k
  if (components.hash_start != url_components::omitted) {
8686
0
    components.hash_start += difference;
8687
0
  }
8688
1.37k
  ADA_ASSERT_TRUE(validate());
8689
1.37k
}
8690
8691
0
inline void url_aggregator::append_base_pathname(const std::string_view input) {
8692
0
  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
8693
0
          "\n", to_diagram());
8694
0
  ADA_ASSERT_TRUE(validate());
8695
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8696
#if ADA_DEVELOPMENT_CHECKS
8697
  // computing the expected password.
8698
  std::string path_expected(get_pathname());
8699
  path_expected.append(input);
8700
#endif  // ADA_DEVELOPMENT_CHECKS
8701
0
  uint32_t ending_index = uint32_t(buffer.size());
8702
0
  if (components.search_start != url_components::omitted) {
8703
0
    ending_index = components.search_start;
8704
0
  } else if (components.hash_start != url_components::omitted) {
8705
0
    ending_index = components.hash_start;
8706
0
  }
8707
0
  buffer.insert(ending_index, input);
8708
8709
0
  if (components.search_start != url_components::omitted) {
8710
0
    components.search_start += uint32_t(input.size());
8711
0
  }
8712
0
  if (components.hash_start != url_components::omitted) {
8713
0
    components.hash_start += uint32_t(input.size());
8714
0
  }
8715
#if ADA_DEVELOPMENT_CHECKS
8716
  std::string path_after = std::string(get_pathname());
8717
  ADA_ASSERT_EQUAL(
8718
      path_expected, path_after,
8719
      "append_base_pathname problem after inserting " + std::string(input));
8720
#endif  // ADA_DEVELOPMENT_CHECKS
8721
0
  ADA_ASSERT_TRUE(validate());
8722
0
}
8723
8724
0
inline void url_aggregator::update_base_username(const std::string_view input) {
8725
0
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8726
0
          "\n", to_diagram());
8727
0
  ADA_ASSERT_TRUE(validate());
8728
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8729
8730
0
  add_authority_slashes_if_needed();
8731
8732
0
  bool has_password = has_non_empty_password();
8733
0
  bool host_starts_with_at = buffer.size() > components.host_start &&
8734
0
                             buffer[components.host_start] == '@';
8735
0
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8736
0
                                     components.username_end, input);
8737
8738
0
  components.username_end += diff;
8739
0
  components.host_start += diff;
8740
8741
0
  if (!input.empty() && !host_starts_with_at) {
8742
0
    buffer.insert(components.host_start, "@");
8743
0
    diff++;
8744
0
  } else if (input.empty() && host_starts_with_at && !has_password) {
8745
    // Input is empty, there is no password, and we need to remove "@" from
8746
    // hostname
8747
0
    buffer.erase(components.host_start, 1);
8748
0
    diff--;
8749
0
  }
8750
8751
0
  components.host_end += diff;
8752
0
  components.pathname_start += diff;
8753
0
  if (components.search_start != url_components::omitted) {
8754
0
    components.search_start += diff;
8755
0
  }
8756
0
  if (components.hash_start != url_components::omitted) {
8757
0
    components.hash_start += diff;
8758
0
  }
8759
0
  ADA_ASSERT_TRUE(validate());
8760
0
}
8761
8762
2.44k
inline void url_aggregator::append_base_username(const std::string_view input) {
8763
2.44k
  ada_log("url_aggregator::append_base_username ", input);
8764
2.44k
  ADA_ASSERT_TRUE(validate());
8765
2.44k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8766
#if ADA_DEVELOPMENT_CHECKS
8767
  // computing the expected password.
8768
  std::string username_expected(get_username());
8769
  username_expected.append(input);
8770
#endif  // ADA_DEVELOPMENT_CHECKS
8771
2.44k
  add_authority_slashes_if_needed();
8772
8773
  // If input is empty, do nothing.
8774
2.44k
  if (input.empty()) {
8775
1.07k
    return;
8776
1.07k
  }
8777
8778
1.36k
  uint32_t difference = uint32_t(input.size());
8779
1.36k
  buffer.insert(components.username_end, input);
8780
1.36k
  components.username_end += difference;
8781
1.36k
  components.host_start += difference;
8782
8783
1.36k
  if (buffer[components.host_start] != '@' &&
8784
168
      components.host_start != components.host_end) {
8785
168
    buffer.insert(components.host_start, "@");
8786
168
    difference++;
8787
168
  }
8788
8789
1.36k
  components.host_end += difference;
8790
1.36k
  components.pathname_start += difference;
8791
1.36k
  if (components.search_start != url_components::omitted) {
8792
0
    components.search_start += difference;
8793
0
  }
8794
1.36k
  if (components.hash_start != url_components::omitted) {
8795
0
    components.hash_start += difference;
8796
0
  }
8797
#if ADA_DEVELOPMENT_CHECKS
8798
  std::string username_after(get_username());
8799
  ADA_ASSERT_EQUAL(
8800
      username_expected, username_after,
8801
      "append_base_username problem after inserting " + std::string(input));
8802
#endif  // ADA_DEVELOPMENT_CHECKS
8803
1.36k
  ADA_ASSERT_TRUE(validate());
8804
1.36k
}
8805
8806
0
constexpr void url_aggregator::clear_password() {
8807
0
  ada_log("url_aggregator::clear_password ", to_string());
8808
0
  ADA_ASSERT_TRUE(validate());
8809
0
  if (!has_password()) {
8810
0
    return;
8811
0
  }
8812
8813
0
  uint32_t diff = components.host_start - components.username_end;
8814
0
  buffer.erase(components.username_end, diff);
8815
0
  components.host_start -= diff;
8816
0
  components.host_end -= diff;
8817
0
  components.pathname_start -= diff;
8818
0
  if (components.search_start != url_components::omitted) {
8819
0
    components.search_start -= diff;
8820
0
  }
8821
0
  if (components.hash_start != url_components::omitted) {
8822
0
    components.hash_start -= diff;
8823
0
  }
8824
0
}
8825
8826
0
inline void url_aggregator::update_base_password(const std::string_view input) {
8827
0
  ada_log("url_aggregator::update_base_password ", input);
8828
0
  ADA_ASSERT_TRUE(validate());
8829
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8830
8831
0
  add_authority_slashes_if_needed();
8832
8833
  // TODO: Optimization opportunity. Merge the following removal functions.
8834
0
  if (input.empty()) {
8835
0
    clear_password();
8836
8837
    // Remove username too, if it is empty.
8838
0
    if (!has_non_empty_username()) {
8839
0
      update_base_username("");
8840
0
    }
8841
8842
0
    return;
8843
0
  }
8844
8845
0
  bool password_exists = has_password();
8846
0
  uint32_t difference = uint32_t(input.size());
8847
8848
0
  if (password_exists) {
8849
0
    difference = replace_and_resize(components.username_end + 1,
8850
0
                                    components.host_start, input);
8851
0
  } else {
8852
0
    buffer.insert(components.username_end, input.size() + 1, ':');
8853
0
    std::memmove(buffer.data() + components.username_end + 1, input.data(),
8854
0
                 input.size());
8855
0
    difference++;
8856
0
  }
8857
0
  components.host_start += difference;
8858
8859
  // The following line is required to add "@" to hostname. When updating
8860
  // password if hostname does not start with "@", it is "update_base_password"s
8861
  // responsibility to set it.
8862
0
  if (buffer[components.host_start] != '@') {
8863
0
    buffer.insert(components.host_start, "@");
8864
0
    difference++;
8865
0
  }
8866
8867
0
  components.host_end += difference;
8868
0
  components.pathname_start += difference;
8869
0
  if (components.search_start != url_components::omitted) {
8870
0
    components.search_start += difference;
8871
0
  }
8872
0
  if (components.hash_start != url_components::omitted) {
8873
0
    components.hash_start += difference;
8874
0
  }
8875
0
  ADA_ASSERT_TRUE(validate());
8876
0
}
8877
8878
928
inline void url_aggregator::append_base_password(const std::string_view input) {
8879
928
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8880
928
          "\n", to_diagram());
8881
928
  ADA_ASSERT_TRUE(validate());
8882
928
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8883
#if ADA_DEVELOPMENT_CHECKS
8884
  // computing the expected password.
8885
  std::string password_expected = std::string(get_password());
8886
  password_expected.append(input);
8887
#endif  // ADA_DEVELOPMENT_CHECKS
8888
928
  add_authority_slashes_if_needed();
8889
8890
  // If input is empty, do nothing.
8891
928
  if (input.empty()) {
8892
390
    return;
8893
390
  }
8894
8895
538
  uint32_t difference = uint32_t(input.size());
8896
538
  if (has_password()) {
8897
480
    buffer.insert(components.host_start, input);
8898
480
  } else {
8899
58
    difference++;  // Increment for ":"
8900
58
    buffer.insert(components.username_end, ":");
8901
58
    buffer.insert(components.username_end + 1, input);
8902
58
  }
8903
538
  components.host_start += difference;
8904
8905
  // The following line is required to add "@" to hostname. When updating
8906
  // password if hostname does not start with "@", it is "append_base_password"s
8907
  // responsibility to set it.
8908
538
  if (buffer[components.host_start] != '@') {
8909
40
    buffer.insert(components.host_start, "@");
8910
40
    difference++;
8911
40
  }
8912
8913
538
  components.host_end += difference;
8914
538
  components.pathname_start += difference;
8915
538
  if (components.search_start != url_components::omitted) {
8916
0
    components.search_start += difference;
8917
0
  }
8918
538
  if (components.hash_start != url_components::omitted) {
8919
0
    components.hash_start += difference;
8920
0
  }
8921
#if ADA_DEVELOPMENT_CHECKS
8922
  std::string password_after(get_password());
8923
  ADA_ASSERT_EQUAL(
8924
      password_expected, password_after,
8925
      "append_base_password problem after inserting " + std::string(input));
8926
#endif  // ADA_DEVELOPMENT_CHECKS
8927
538
  ADA_ASSERT_TRUE(validate());
8928
538
}
8929
8930
48
inline void url_aggregator::update_base_port(uint32_t input) {
8931
48
  ada_log("url_aggregator::update_base_port");
8932
48
  ADA_ASSERT_TRUE(validate());
8933
48
  if (input == url_components::omitted) {
8934
0
    clear_port();
8935
0
    return;
8936
0
  }
8937
  // calling std::to_string(input.value()) is unfortunate given that the port
8938
  // value is probably already available as a string.
8939
48
  std::string value = helpers::concat(":", std::to_string(input));
8940
48
  uint32_t difference = uint32_t(value.size());
8941
8942
48
  if (components.port != url_components::omitted) {
8943
0
    difference -= components.pathname_start - components.host_end;
8944
0
    buffer.erase(components.host_end,
8945
0
                 components.pathname_start - components.host_end);
8946
0
  }
8947
8948
48
  buffer.insert(components.host_end, value);
8949
48
  components.pathname_start += difference;
8950
48
  if (components.search_start != url_components::omitted) {
8951
0
    components.search_start += difference;
8952
0
  }
8953
48
  if (components.hash_start != url_components::omitted) {
8954
0
    components.hash_start += difference;
8955
0
  }
8956
48
  components.port = input;
8957
48
  ADA_ASSERT_TRUE(validate());
8958
48
}
8959
8960
45
inline void url_aggregator::clear_port() {
8961
45
  ada_log("url_aggregator::clear_port");
8962
45
  ADA_ASSERT_TRUE(validate());
8963
45
  if (components.port == url_components::omitted) {
8964
45
    return;
8965
45
  }
8966
0
  uint32_t length = components.pathname_start - components.host_end;
8967
0
  buffer.erase(components.host_end, length);
8968
0
  components.pathname_start -= length;
8969
0
  if (components.search_start != url_components::omitted) {
8970
0
    components.search_start -= length;
8971
0
  }
8972
0
  if (components.hash_start != url_components::omitted) {
8973
0
    components.hash_start -= length;
8974
0
  }
8975
0
  components.port = url_components::omitted;
8976
0
  ADA_ASSERT_TRUE(validate());
8977
0
}
8978
8979
0
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8980
0
  ada_log("url_aggregator::retrieve_base_port");
8981
0
  return components.port;
8982
0
}
8983
8984
0
inline void url_aggregator::clear_search() {
8985
0
  ada_log("url_aggregator::clear_search");
8986
0
  ADA_ASSERT_TRUE(validate());
8987
0
  if (components.search_start == url_components::omitted) {
8988
0
    return;
8989
0
  }
8990
8991
0
  if (components.hash_start == url_components::omitted) {
8992
0
    buffer.resize(components.search_start);
8993
0
  } else {
8994
0
    buffer.erase(components.search_start,
8995
0
                 components.hash_start - components.search_start);
8996
0
    components.hash_start = components.search_start;
8997
0
  }
8998
8999
0
  components.search_start = url_components::omitted;
9000
9001
#if ADA_DEVELOPMENT_CHECKS
9002
  ADA_ASSERT_EQUAL(get_search(), "",
9003
                   "search should have been cleared on buffer=" + buffer +
9004
                       " with " + components.to_string() + "\n" + to_diagram());
9005
#endif
9006
0
  ADA_ASSERT_TRUE(validate());
9007
0
}
9008
9009
0
inline void url_aggregator::clear_hash() {
9010
0
  ada_log("url_aggregator::clear_hash");
9011
0
  ADA_ASSERT_TRUE(validate());
9012
0
  if (components.hash_start == url_components::omitted) {
9013
0
    return;
9014
0
  }
9015
0
  buffer.resize(components.hash_start);
9016
0
  components.hash_start = url_components::omitted;
9017
9018
#if ADA_DEVELOPMENT_CHECKS
9019
  ADA_ASSERT_EQUAL(get_hash(), "",
9020
                   "hash should have been cleared on buffer=" + buffer +
9021
                       " with " + components.to_string() + "\n" + to_diagram());
9022
#endif
9023
0
  ADA_ASSERT_TRUE(validate());
9024
0
}
9025
9026
0
constexpr void url_aggregator::clear_pathname() {
9027
0
  ada_log("url_aggregator::clear_pathname");
9028
0
  ADA_ASSERT_TRUE(validate());
9029
0
  uint32_t ending_index = uint32_t(buffer.size());
9030
0
  if (components.search_start != url_components::omitted) {
9031
0
    ending_index = components.search_start;
9032
0
  } else if (components.hash_start != url_components::omitted) {
9033
0
    ending_index = components.hash_start;
9034
0
  }
9035
0
  uint32_t pathname_length = ending_index - components.pathname_start;
9036
0
  buffer.erase(components.pathname_start, pathname_length);
9037
0
  uint32_t difference = pathname_length;
9038
0
  if (components.pathname_start == components.host_end + 2 &&
9039
0
      buffer[components.host_end] == '/' &&
9040
0
      buffer[components.host_end + 1] == '.') {
9041
0
    components.pathname_start -= 2;
9042
0
    buffer.erase(components.host_end, 2);
9043
0
    difference += 2;
9044
0
  }
9045
0
  if (components.search_start != url_components::omitted) {
9046
0
    components.search_start -= difference;
9047
0
  }
9048
0
  if (components.hash_start != url_components::omitted) {
9049
0
    components.hash_start -= difference;
9050
0
  }
9051
0
  ada_log("url_aggregator::clear_pathname completed, running checks...");
9052
#if ADA_DEVELOPMENT_CHECKS
9053
  ADA_ASSERT_EQUAL(get_pathname(), "",
9054
                   "pathname should have been cleared on buffer=" + buffer +
9055
                       " with " + components.to_string() + "\n" + to_diagram());
9056
#endif
9057
0
  ADA_ASSERT_TRUE(validate());
9058
0
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
9059
0
}
9060
9061
0
constexpr void url_aggregator::clear_hostname() {
9062
0
  ada_log("url_aggregator::clear_hostname");
9063
0
  ADA_ASSERT_TRUE(validate());
9064
0
  if (!has_authority()) {
9065
0
    return;
9066
0
  }
9067
0
  ADA_ASSERT_TRUE(has_authority());
9068
9069
0
  uint32_t hostname_length = components.host_end - components.host_start;
9070
0
  uint32_t start = components.host_start;
9071
9072
  // If hostname starts with "@", we should not remove that character.
9073
0
  if (hostname_length > 0 && buffer[start] == '@') {
9074
0
    start++;
9075
0
    hostname_length--;
9076
0
  }
9077
0
  buffer.erase(start, hostname_length);
9078
0
  components.host_end = start;
9079
0
  components.pathname_start -= hostname_length;
9080
0
  if (components.search_start != url_components::omitted) {
9081
0
    components.search_start -= hostname_length;
9082
0
  }
9083
0
  if (components.hash_start != url_components::omitted) {
9084
0
    components.hash_start -= hostname_length;
9085
0
  }
9086
#if ADA_DEVELOPMENT_CHECKS
9087
  ADA_ASSERT_EQUAL(get_hostname(), "",
9088
                   "hostname should have been cleared on buffer=" + buffer +
9089
                       " with " + components.to_string() + "\n" + to_diagram());
9090
#endif
9091
0
  ADA_ASSERT_TRUE(has_authority());
9092
0
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
9093
0
                   "hostname should have been cleared on buffer=" + buffer +
9094
0
                       " with " + components.to_string() + "\n" + to_diagram());
9095
0
  ADA_ASSERT_TRUE(validate());
9096
0
}
9097
9098
0
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
9099
0
  ada_log("url_aggregator::has_hash");
9100
0
  return components.hash_start != url_components::omitted;
9101
0
}
9102
9103
0
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
9104
0
  ada_log("url_aggregator::has_search");
9105
0
  return components.search_start != url_components::omitted;
9106
0
}
9107
9108
0
constexpr bool url_aggregator::has_credentials() const noexcept {
9109
0
  ada_log("url_aggregator::has_credentials");
9110
0
  return has_non_empty_username() || has_non_empty_password();
9111
0
}
9112
9113
0
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
9114
0
  ada_log("url_aggregator::cannot_have_credentials_or_port");
9115
0
  return type == ada::scheme::type::FILE ||
9116
0
         components.host_start == components.host_end;
9117
0
}
9118
9119
[[nodiscard]] ada_really_inline const ada::url_components&
9120
0
url_aggregator::get_components() const noexcept {
9121
0
  return components;
9122
0
}
9123
9124
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
9125
5.27k
    const noexcept {
9126
5.27k
  ada_log("url_aggregator::has_authority");
9127
  // Performance: instead of doing this potentially expensive check, we could
9128
  // have a boolean in the struct.
9129
5.27k
  return components.protocol_end + 2 <= components.host_start &&
9130
3.42k
         buffer[components.protocol_end] == '/' &&
9131
3.42k
         buffer[components.protocol_end + 1] == '/';
9132
5.27k
}
9133
9134
5.09k
inline void ada::url_aggregator::add_authority_slashes_if_needed() {
9135
5.09k
  ada_log("url_aggregator::add_authority_slashes_if_needed");
9136
5.09k
  ADA_ASSERT_TRUE(validate());
9137
  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
9138
  // to insert
9139
  // `//` initially to the buffer, since it depends on the hostname existence.
9140
5.09k
  if (has_authority()) {
9141
3.24k
    return;
9142
3.24k
  }
9143
  // Performance: the common case is components.protocol_end == buffer.size()
9144
  // Optimization opportunity: in many cases, the "//" is part of the input and
9145
  // the insert could be fused with another insert.
9146
1.84k
  buffer.insert(components.protocol_end, "//");
9147
1.84k
  components.username_end += 2;
9148
1.84k
  components.host_start += 2;
9149
1.84k
  components.host_end += 2;
9150
1.84k
  components.pathname_start += 2;
9151
1.84k
  if (components.search_start != url_components::omitted) {
9152
0
    components.search_start += 2;
9153
0
  }
9154
1.84k
  if (components.hash_start != url_components::omitted) {
9155
0
    components.hash_start += 2;
9156
0
  }
9157
1.84k
  ADA_ASSERT_TRUE(validate());
9158
1.84k
}
9159
9160
12.1k
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
9161
12.1k
  buffer.reserve(capacity);
9162
12.1k
}
9163
9164
0
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
9165
0
  ada_log("url_aggregator::has_non_empty_username");
9166
0
  return components.protocol_end + 2 < components.username_end;
9167
0
}
9168
9169
0
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
9170
0
  ada_log("url_aggregator::has_non_empty_password");
9171
0
  return components.host_start > components.username_end;
9172
0
}
9173
9174
538
constexpr bool url_aggregator::has_password() const noexcept {
9175
538
  ada_log("url_aggregator::has_password");
9176
  // This function does not care about the length of the password
9177
538
  return components.host_start > components.username_end &&
9178
480
         buffer[components.username_end] == ':';
9179
538
}
9180
9181
0
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
9182
0
  if (!has_hostname()) {
9183
0
    return false;
9184
0
  }
9185
0
  if (components.host_start == components.host_end) {
9186
0
    return true;
9187
0
  }
9188
0
  if (components.host_end > components.host_start + 1) {
9189
0
    return false;
9190
0
  }
9191
0
  return components.username_end != components.host_start;
9192
0
}
9193
9194
0
constexpr bool url_aggregator::has_hostname() const noexcept {
9195
0
  return has_authority();
9196
0
}
9197
9198
0
constexpr bool url_aggregator::has_port() const noexcept {
9199
0
  ada_log("url_aggregator::has_port");
9200
  // A URL cannot have a username/password/port if its host is null or the empty
9201
  // string, or its scheme is "file".
9202
0
  return has_hostname() && components.pathname_start != components.host_end;
9203
0
}
9204
9205
1.18k
[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
9206
  // If url's host is null, url does not have an opaque path, url's path's size
9207
  // is greater than 1, and url's path[0] is the empty string, then append
9208
  // U+002F (/) followed by U+002E (.) to output.
9209
1.18k
  ada_log("url_aggregator::has_dash_dot");
9210
#if ADA_DEVELOPMENT_CHECKS
9211
  // If pathname_start and host_end are exactly two characters apart, then we
9212
  // either have a one-digit port such as http://test.com:5?param=1 or else we
9213
  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
9214
  if (components.pathname_start == components.host_end + 2) {
9215
    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
9216
                     buffer[components.host_end + 1] == '.') ||
9217
                    (buffer[components.host_end] == ':' &&
9218
                     checkers::is_digit(buffer[components.host_end + 1])));
9219
  }
9220
  if (components.pathname_start == components.host_end + 2 &&
9221
      buffer[components.host_end] == '/' &&
9222
      buffer[components.host_end + 1] == '.') {
9223
    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
9224
    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
9225
    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
9226
  }
9227
#endif
9228
  // Performance: it should be uncommon for components.pathname_start ==
9229
  // components.host_end + 2 to be true. So we put this check first in the
9230
  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
9231
  // more expensive, but should be uncommon.
9232
1.18k
  return components.pathname_start == components.host_end + 2 &&
9233
19
         !has_opaque_path && buffer[components.host_end] == '/' &&
9234
0
         buffer[components.host_end + 1] == '.';
9235
1.18k
}
9236
9237
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
9238
0
    const noexcept ada_lifetime_bound {
9239
0
  ada_log("url_aggregator::get_href");
9240
0
  return buffer;
9241
0
}
9242
9243
0
[[nodiscard]] constexpr size_t url_aggregator::get_href_size() const noexcept {
9244
0
  return buffer.size();
9245
0
}
9246
9247
ada_really_inline size_t
9248
604
url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
9249
604
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
9250
604
  if (!view.empty() && view[0] == '-') {
9251
1
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
9252
1
    is_valid = false;
9253
1
    return 0;
9254
1
  }
9255
603
  uint16_t parsed_port{};
9256
603
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
9257
603
  if (r.ec == std::errc::result_out_of_range) {
9258
17
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
9259
17
    is_valid = false;
9260
17
    return 0;
9261
17
  }
9262
586
  ada_log("parse_port: ", parsed_port);
9263
586
  const size_t consumed = size_t(r.ptr - view.data());
9264
586
  ada_log("parse_port: consumed ", consumed);
9265
586
  if (check_trailing_content) {
9266
586
    is_valid &=
9267
586
        (consumed == view.size() || view[consumed] == '/' ||
9268
505
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
9269
586
  }
9270
586
  ada_log("parse_port: is_valid = ", is_valid);
9271
586
  if (is_valid) {
9272
93
    ada_log("parse_port", r.ec == std::errc());
9273
    // scheme_default_port can return 0, and we should allow 0 as a base port.
9274
93
    auto default_port = scheme_default_port();
9275
93
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
9276
93
                         (default_port != parsed_port);
9277
93
    if (r.ec == std::errc() && is_port_valid) {
9278
48
      update_base_port(parsed_port);
9279
48
    } else {
9280
45
      clear_port();
9281
45
    }
9282
93
  }
9283
586
  return consumed;
9284
603
}
9285
9286
0
constexpr void url_aggregator::set_protocol_as_file() {
9287
0
  ada_log("url_aggregator::set_protocol_as_file ");
9288
0
  ADA_ASSERT_TRUE(validate());
9289
0
  type = ada::scheme::type::FILE;
9290
  // next line could overflow but unsigned arithmetic has well-defined
9291
  // overflows.
9292
0
  uint32_t new_difference = 5 - components.protocol_end;
9293
9294
0
  if (buffer.empty()) {
9295
0
    buffer.append("file:");
9296
0
  } else {
9297
0
    buffer.erase(0, components.protocol_end);
9298
0
    buffer.insert(0, "file:");
9299
0
  }
9300
0
  components.protocol_end = 5;
9301
9302
  // Update the rest of the components.
9303
0
  components.username_end += new_difference;
9304
0
  components.host_start += new_difference;
9305
0
  components.host_end += new_difference;
9306
0
  components.pathname_start += new_difference;
9307
0
  if (components.search_start != url_components::omitted) {
9308
0
    components.search_start += new_difference;
9309
0
  }
9310
0
  if (components.hash_start != url_components::omitted) {
9311
0
    components.hash_start += new_difference;
9312
0
  }
9313
0
  ADA_ASSERT_TRUE(validate());
9314
0
}
9315
9316
2.52k
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
9317
2.52k
  if (!is_valid) {
9318
0
    return true;
9319
0
  }
9320
2.52k
  if (!components.check_offset_consistency()) {
9321
0
    ada_log("url_aggregator::validate inconsistent components \n",
9322
0
            to_diagram());
9323
0
    return false;
9324
0
  }
9325
  // We have a credible components struct, but let us investivate more
9326
  // carefully:
9327
  /**
9328
   * https://user:pass@example.com:1234/foo/bar?baz#quux
9329
   *       |     |    |          | ^^^^|       |   |
9330
   *       |     |    |          | |   |       |   `----- hash_start
9331
   *       |     |    |          | |   |       `--------- search_start
9332
   *       |     |    |          | |   `----------------- pathname_start
9333
   *       |     |    |          | `--------------------- port
9334
   *       |     |    |          `----------------------- host_end
9335
   *       |     |    `---------------------------------- host_start
9336
   *       |     `--------------------------------------- username_end
9337
   *       `--------------------------------------------- protocol_end
9338
   */
9339
2.52k
  if (components.protocol_end == url_components::omitted) {
9340
0
    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
9341
0
    return false;
9342
0
  }
9343
2.52k
  if (components.username_end == url_components::omitted) {
9344
0
    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
9345
0
    return false;
9346
0
  }
9347
2.52k
  if (components.host_start == url_components::omitted) {
9348
0
    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
9349
0
    return false;
9350
0
  }
9351
2.52k
  if (components.host_end == url_components::omitted) {
9352
0
    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
9353
0
    return false;
9354
0
  }
9355
2.52k
  if (components.pathname_start == url_components::omitted) {
9356
0
    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
9357
0
    return false;
9358
0
  }
9359
9360
2.52k
  if (components.protocol_end > buffer.size()) {
9361
0
    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
9362
0
    return false;
9363
0
  }
9364
2.52k
  if (components.username_end > buffer.size()) {
9365
0
    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
9366
0
    return false;
9367
0
  }
9368
2.52k
  if (components.host_start > buffer.size()) {
9369
0
    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
9370
0
    return false;
9371
0
  }
9372
2.52k
  if (components.host_end > buffer.size()) {
9373
0
    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
9374
0
    return false;
9375
0
  }
9376
2.52k
  if (components.pathname_start > buffer.size()) {
9377
0
    ada_log("url_aggregator::validate pathname_start overflow \n",
9378
0
            to_diagram());
9379
0
    return false;
9380
0
  }
9381
9382
2.52k
  if (components.protocol_end > 0) {
9383
2.52k
    if (buffer[components.protocol_end - 1] != ':') {
9384
0
      ada_log(
9385
0
          "url_aggregator::validate missing : at the end of the protocol \n",
9386
0
          to_diagram());
9387
0
      return false;
9388
0
    }
9389
2.52k
  }
9390
9391
2.52k
  if (components.username_end != buffer.size() &&
9392
2.52k
      components.username_end > components.protocol_end + 2) {
9393
76
    if (buffer[components.username_end] != ':' &&
9394
68
        buffer[components.username_end] != '@') {
9395
0
      ada_log(
9396
0
          "url_aggregator::validate missing : or @ at the end of the username "
9397
0
          "\n",
9398
0
          to_diagram());
9399
0
      return false;
9400
0
    }
9401
76
  }
9402
9403
2.52k
  if (components.host_start != buffer.size()) {
9404
2.52k
    if (components.host_start > components.username_end) {
9405
18
      if (buffer[components.host_start] != '@') {
9406
0
        ada_log(
9407
0
            "url_aggregator::validate missing @ at the end of the password \n",
9408
0
            to_diagram());
9409
0
        return false;
9410
0
      }
9411
2.50k
    } else if (components.host_start == components.username_end &&
9412
2.50k
               components.host_end > components.host_start) {
9413
2.50k
      if (components.host_start == components.protocol_end + 2) {
9414
2.43k
        if (buffer[components.protocol_end] != '/' ||
9415
2.43k
            buffer[components.protocol_end + 1] != '/') {
9416
0
          ada_log(
9417
0
              "url_aggregator::validate missing // between protocol and host "
9418
0
              "\n",
9419
0
              to_diagram());
9420
0
          return false;
9421
0
        }
9422
2.43k
      } else {
9423
68
        if (components.host_start > components.protocol_end &&
9424
68
            buffer[components.host_start] != '@') {
9425
0
          ada_log(
9426
0
              "url_aggregator::validate missing @ at the end of the username "
9427
0
              "\n",
9428
0
              to_diagram());
9429
0
          return false;
9430
0
        }
9431
68
      }
9432
2.50k
    } else {
9433
0
      if (components.host_end != components.host_start) {
9434
0
        ada_log("url_aggregator::validate expected omitted host \n",
9435
0
                to_diagram());
9436
0
        return false;
9437
0
      }
9438
0
    }
9439
2.52k
  }
9440
2.52k
  if (components.host_end != buffer.size() &&
9441
2.52k
      components.pathname_start > components.host_end) {
9442
175
    if (components.pathname_start == components.host_end + 2 &&
9443
34
        buffer[components.host_end] == '/' &&
9444
0
        buffer[components.host_end + 1] == '.') {
9445
0
      if (components.pathname_start + 1 >= buffer.size() ||
9446
0
          buffer[components.pathname_start] != '/' ||
9447
0
          buffer[components.pathname_start + 1] != '/') {
9448
0
        ada_log(
9449
0
            "url_aggregator::validate expected the path to begin with // \n",
9450
0
            to_diagram());
9451
0
        return false;
9452
0
      }
9453
175
    } else if (buffer[components.host_end] != ':') {
9454
0
      ada_log("url_aggregator::validate missing : at the port \n",
9455
0
              to_diagram());
9456
0
      return false;
9457
0
    }
9458
175
  }
9459
2.52k
  if (components.pathname_start != buffer.size() &&
9460
2.52k
      components.pathname_start < components.search_start &&
9461
2.52k
      components.pathname_start < components.hash_start && !has_opaque_path) {
9462
2.52k
    if (buffer[components.pathname_start] != '/') {
9463
0
      ada_log("url_aggregator::validate missing / at the path \n",
9464
0
              to_diagram());
9465
0
      return false;
9466
0
    }
9467
2.52k
  }
9468
2.52k
  if (components.search_start != url_components::omitted) {
9469
756
    if (buffer[components.search_start] != '?') {
9470
0
      ada_log("url_aggregator::validate missing ? at the search \n",
9471
0
              to_diagram());
9472
0
      return false;
9473
0
    }
9474
756
  }
9475
2.52k
  if (components.hash_start != url_components::omitted) {
9476
382
    if (buffer[components.hash_start] != '#') {
9477
0
      ada_log("url_aggregator::validate missing # at the hash \n",
9478
0
              to_diagram());
9479
0
      return false;
9480
0
    }
9481
382
  }
9482
9483
2.52k
  return true;
9484
2.52k
}
9485
9486
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() const
9487
1.13k
    ada_lifetime_bound {
9488
1.13k
  ada_log("url_aggregator::get_pathname pathname_start = ",
9489
1.13k
          components.pathname_start, " buffer.size() = ", buffer.size(),
9490
1.13k
          " components.search_start = ", components.search_start,
9491
1.13k
          " components.hash_start = ", components.hash_start);
9492
1.13k
  auto ending_index = uint32_t(buffer.size());
9493
1.13k
  if (components.search_start != url_components::omitted) {
9494
0
    ending_index = components.search_start;
9495
1.13k
  } else if (components.hash_start != url_components::omitted) {
9496
0
    ending_index = components.hash_start;
9497
0
  }
9498
1.13k
  return helpers::substring(buffer, components.pathname_start, ending_index);
9499
1.13k
}
9500
9501
inline std::ostream& operator<<(std::ostream& out,
9502
0
                                const ada::url_aggregator& u) {
9503
0
  return out << u.to_string();
9504
0
}
9505
9506
0
void url_aggregator::update_host_to_base_host(const std::string_view input) {
9507
0
  ada_log("url_aggregator::update_host_to_base_host ", input);
9508
0
  ADA_ASSERT_TRUE(validate());
9509
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
9510
0
  if (type != ada::scheme::type::FILE) {
9511
    // Let host be the result of host parsing host_view with url is not special.
9512
0
    if (input.empty() && !is_special()) {
9513
0
      if (has_hostname()) {
9514
0
        clear_hostname();
9515
0
      } else if (has_dash_dot()) {
9516
0
        add_authority_slashes_if_needed();
9517
0
        delete_dash_dot();
9518
0
      }
9519
0
      return;
9520
0
    }
9521
0
  }
9522
0
  update_base_hostname(input);
9523
0
  ADA_ASSERT_TRUE(validate());
9524
0
  return;
9525
0
}
9526
}  // namespace ada
9527
9528
#endif  // ADA_URL_AGGREGATOR_INL_H
9529
/* end file include/ada/url_aggregator-inl.h */
9530
/* begin file include/ada/url_search_params.h */
9531
/**
9532
 * @file url_search_params.h
9533
 * @brief URL query string parameter manipulation.
9534
 *
9535
 * This file provides the `url_search_params` class for parsing, manipulating,
9536
 * and serializing URL query strings. It implements the URLSearchParams API
9537
 * from the WHATWG URL Standard.
9538
 *
9539
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9540
 */
9541
#ifndef ADA_URL_SEARCH_PARAMS_H
9542
#define ADA_URL_SEARCH_PARAMS_H
9543
9544
#include <cstddef>
9545
#include <optional>
9546
#include <string>
9547
#include <string_view>
9548
#include <utility>
9549
#include <vector>
9550
9551
namespace ada {
9552
9553
/**
9554
 * @brief Iterator types for url_search_params iteration.
9555
 */
9556
enum class url_search_params_iter_type {
9557
  KEYS,    /**< Iterate over parameter keys only */
9558
  VALUES,  /**< Iterate over parameter values only */
9559
  ENTRIES, /**< Iterate over key-value pairs */
9560
};
9561
9562
template <typename T, url_search_params_iter_type Type>
9563
struct url_search_params_iter;
9564
9565
/** Type alias for a key-value pair of string views. */
9566
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
9567
9568
/** Iterator over search parameter keys. */
9569
using url_search_params_keys_iter =
9570
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
9571
/** Iterator over search parameter values. */
9572
using url_search_params_values_iter =
9573
    url_search_params_iter<std::string_view,
9574
                           url_search_params_iter_type::VALUES>;
9575
/** Iterator over search parameter key-value pairs. */
9576
using url_search_params_entries_iter =
9577
    url_search_params_iter<key_value_view_pair,
9578
                           url_search_params_iter_type::ENTRIES>;
9579
9580
/**
9581
 * @brief Class for parsing and manipulating URL query strings.
9582
 *
9583
 * The `url_search_params` class provides methods to parse, modify, and
9584
 * serialize URL query parameters (the part after '?' in a URL). It handles
9585
 * percent-encoding and decoding automatically.
9586
 *
9587
 * All string inputs must be valid UTF-8. The caller is responsible for
9588
 * ensuring UTF-8 validity.
9589
 *
9590
 * Construction and `reset` refuse query strings longer than
9591
 * `get_max_input_length()` (the object is left empty). Individual `append` /
9592
 * `set` calls are not length-capped.
9593
 *
9594
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9595
 */
9596
struct url_search_params {
9597
  url_search_params() = default;
9598
9599
  /**
9600
   * Constructs url_search_params by parsing a query string.
9601
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9602
   *        If longer than `get_max_input_length()`, the object stays empty.
9603
   */
9604
0
  explicit url_search_params(const std::string_view input) {
9605
0
    initialize(input);
9606
0
  }
9607
9608
  url_search_params(const url_search_params& u) = default;
9609
0
  url_search_params(url_search_params&& u) noexcept = default;
9610
  url_search_params& operator=(url_search_params&& u) noexcept = default;
9611
  url_search_params& operator=(const url_search_params& u) = default;
9612
0
  ~url_search_params() = default;
9613
9614
  /**
9615
   * Returns the number of key-value pairs.
9616
   * @return The total count of parameters.
9617
   */
9618
  [[nodiscard]] inline size_t size() const noexcept;
9619
9620
  /**
9621
   * Appends a new key-value pair to the parameter list.
9622
   * @param key The parameter name (must be valid UTF-8).
9623
   * @param value The parameter value (must be valid UTF-8).
9624
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9625
   */
9626
  inline void append(std::string_view key, std::string_view value);
9627
9628
  /**
9629
   * Removes all pairs with the given key.
9630
   * @param key The parameter name to remove.
9631
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9632
   */
9633
  inline void remove(std::string_view key);
9634
9635
  /**
9636
   * Removes all pairs with the given key and value.
9637
   * @param key The parameter name.
9638
   * @param value The parameter value to match.
9639
   */
9640
  inline void remove(std::string_view key, std::string_view value);
9641
9642
  /**
9643
   * Returns the value of the first pair with the given key.
9644
   * @param key The parameter name to search for.
9645
   * @return The value if found, or std::nullopt if not present.
9646
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9647
   */
9648
  inline std::optional<std::string_view> get(std::string_view key);
9649
9650
  /**
9651
   * Returns all values for pairs with the given key.
9652
   * @param key The parameter name to search for.
9653
   * @return A vector of all matching values (may be empty).
9654
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9655
   */
9656
  inline std::vector<std::string> get_all(std::string_view key);
9657
9658
  /**
9659
   * Checks if any pair has the given key.
9660
   * @param key The parameter name to search for.
9661
   * @return `true` if at least one pair has this key.
9662
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9663
   */
9664
  inline bool has(std::string_view key) noexcept;
9665
9666
  /**
9667
   * Checks if any pair matches the given key and value.
9668
   * @param key The parameter name to search for.
9669
   * @param value The parameter value to match.
9670
   * @return `true` if a matching pair exists.
9671
   */
9672
  inline bool has(std::string_view key, std::string_view value) noexcept;
9673
9674
  /**
9675
   * Sets a parameter value, replacing any existing pairs with the same key.
9676
   * @param key The parameter name (must be valid UTF-8).
9677
   * @param value The parameter value (must be valid UTF-8).
9678
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9679
   */
9680
  inline void set(std::string_view key, std::string_view value);
9681
9682
  /**
9683
   * Sorts all key-value pairs by their keys using code unit comparison.
9684
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9685
   */
9686
  inline void sort();
9687
9688
  /**
9689
   * Serializes the parameters to a query string (without leading '?').
9690
   * @return The percent-encoded query string.
9691
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9692
   */
9693
  inline std::string to_string() const;
9694
9695
  /**
9696
   * Returns an iterator over all parameter keys.
9697
   * Keys may repeat if there are duplicate parameters.
9698
   * @return An iterator yielding string_view keys.
9699
   * @note The iterator is invalidated if this object is modified.
9700
   */
9701
  inline url_search_params_keys_iter get_keys();
9702
9703
  /**
9704
   * Returns an iterator over all parameter values.
9705
   * @return An iterator yielding string_view values.
9706
   * @note The iterator is invalidated if this object is modified.
9707
   */
9708
  inline url_search_params_values_iter get_values();
9709
9710
  /**
9711
   * Returns an iterator over all key-value pairs.
9712
   * @return An iterator yielding key-value pair views.
9713
   * @note The iterator is invalidated if this object is modified.
9714
   */
9715
  inline url_search_params_entries_iter get_entries();
9716
9717
  /**
9718
   * C++ style conventional iterator support. const only because we
9719
   * do not really want the params to be modified via the iterator.
9720
   */
9721
0
  inline auto begin() const { return params.begin(); }
9722
0
  inline auto end() const { return params.end(); }
9723
0
  inline auto front() const { return params.front(); }
9724
0
  inline auto back() const { return params.back(); }
9725
0
  inline auto operator[](size_t index) const { return params[index]; }
9726
9727
  /**
9728
   * @private
9729
   * Used to reset the search params to a new input.
9730
   * Used primarily for C API.
9731
   * @param input
9732
   */
9733
  void reset(std::string_view input);
9734
9735
 private:
9736
  typedef std::pair<std::string, std::string> key_value_pair;
9737
  std::vector<key_value_pair> params{};
9738
9739
  /**
9740
   * The init parameter must be valid UTF-8.
9741
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9742
   */
9743
  void initialize(std::string_view init);
9744
9745
  template <typename T, url_search_params_iter_type Type>
9746
  friend struct url_search_params_iter;
9747
};  // url_search_params
9748
9749
/**
9750
 * @brief JavaScript-style iterator for url_search_params.
9751
 *
9752
 * Provides a `next()` method that returns successive values until exhausted.
9753
 * This matches the iterator pattern used in the Web Platform.
9754
 *
9755
 * @tparam T The type of value returned by the iterator.
9756
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9757
 *
9758
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9759
 */
9760
template <typename T, url_search_params_iter_type Type>
9761
struct url_search_params_iter {
9762
0
  inline url_search_params_iter() : params(EMPTY) {}
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::url_search_params_iter()
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::url_search_params_iter()
Unexecuted instantiation: ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::url_search_params_iter()
9763
  url_search_params_iter(const url_search_params_iter& u) = default;
9764
  url_search_params_iter(url_search_params_iter&& u) noexcept = default;
9765
  url_search_params_iter& operator=(url_search_params_iter&& u) noexcept =
9766
      default;
9767
  url_search_params_iter& operator=(const url_search_params_iter& u) = default;
9768
  ~url_search_params_iter() = default;
9769
9770
  /**
9771
   * Returns the next value in the iteration sequence.
9772
   * @return The next value, or std::nullopt if iteration is complete.
9773
   */
9774
  inline std::optional<T> next();
9775
9776
  /**
9777
   * Checks if more values are available.
9778
   * @return `true` if `next()` will return a value, `false` if exhausted.
9779
   */
9780
  inline bool has_next() const;
9781
9782
 private:
9783
  static url_search_params EMPTY;
9784
0
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::url_search_params_iter(ada::url_search_params&)
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::url_search_params_iter(ada::url_search_params&)
Unexecuted instantiation: ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::url_search_params_iter(ada::url_search_params&)
9785
9786
  url_search_params& params;
9787
  size_t pos = 0;
9788
9789
  friend struct url_search_params;
9790
};
9791
9792
}  // namespace ada
9793
#endif
9794
/* end file include/ada/url_search_params.h */
9795
/* begin file include/ada/url_search_params-inl.h */
9796
/**
9797
 * @file url_search_params-inl.h
9798
 * @brief Inline declarations for the URL Search Params
9799
 */
9800
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9801
#define ADA_URL_SEARCH_PARAMS_INL_H
9802
9803
9804
#include <algorithm>
9805
#include <cstddef>
9806
#include <cstdint>
9807
#include <iterator>
9808
#include <optional>
9809
#include <ranges>
9810
#include <string>
9811
#include <string_view>
9812
#include <vector>
9813
9814
namespace ada {
9815
9816
// Declared in implementation.h; used here as a DoS bound on untrusted query
9817
// strings (ada.h includes both headers).
9818
uint32_t get_max_input_length();
9819
9820
// A default, empty url_search_params for use with empty iterators.
9821
template <typename T, ada::url_search_params_iter_type Type>
9822
url_search_params url_search_params_iter<T, Type>::EMPTY;
9823
9824
0
inline void url_search_params::reset(std::string_view input) {
9825
0
  params.clear();
9826
0
  initialize(input);
9827
0
}
9828
9829
0
inline void url_search_params::initialize(std::string_view input) {
9830
0
  if (!input.empty() && input.front() == '?') {
9831
0
    input.remove_prefix(1);
9832
0
  }
9833
0
  if (input.empty()) {
9834
0
    return;
9835
0
  }
9836
  // Refuse overlong query strings (same process-wide cap as URL parsing).
9837
0
  if (input.size() > get_max_input_length()) {
9838
0
    return;
9839
0
  }
9840
9841
0
  params.reserve(size_t(std::count(input.begin(), input.end(), '&')) + 1);
9842
9843
0
  auto process_key_value = [&](const std::string_view current) {
9844
0
    const auto equal = current.find('=');
9845
0
    if (equal == std::string_view::npos) {
9846
0
      params.emplace_back(unicode::form_urlencoded_decode(current), "");
9847
0
    } else {
9848
0
      params.emplace_back(
9849
0
          unicode::form_urlencoded_decode(current.substr(0, equal)),
9850
0
          unicode::form_urlencoded_decode(current.substr(equal + 1)));
9851
0
    }
9852
0
  };
9853
9854
0
  while (!input.empty()) {
9855
0
    const auto ampersand_index = input.find('&');
9856
9857
0
    if (ampersand_index == std::string_view::npos) {
9858
0
      if (!input.empty()) {
9859
0
        process_key_value(input);
9860
0
      }
9861
0
      break;
9862
0
    } else if (ampersand_index != 0) {
9863
0
      process_key_value(input.substr(0, ampersand_index));
9864
0
    }
9865
9866
0
    input.remove_prefix(ampersand_index + 1);
9867
0
  }
9868
0
}
9869
9870
inline void url_search_params::append(const std::string_view key,
9871
0
                                      const std::string_view value) {
9872
0
  params.emplace_back(key, value);
9873
0
}
9874
9875
0
inline size_t url_search_params::size() const noexcept { return params.size(); }
9876
9877
inline std::optional<std::string_view> url_search_params::get(
9878
0
    const std::string_view key) {
9879
0
  auto entry = std::ranges::find_if(
9880
0
      params, [&key](const auto& param) { return param.first == key; });
9881
9882
0
  if (entry == params.end()) {
9883
0
    return std::nullopt;
9884
0
  }
9885
9886
0
  return entry->second;
9887
0
}
9888
9889
inline std::vector<std::string> url_search_params::get_all(
9890
0
    const std::string_view key) {
9891
0
  std::vector<std::string> out{};
9892
9893
0
  for (auto& param : params) {
9894
0
    if (param.first == key) {
9895
0
      out.emplace_back(param.second);
9896
0
    }
9897
0
  }
9898
9899
0
  return out;
9900
0
}
9901
9902
0
inline bool url_search_params::has(const std::string_view key) noexcept {
9903
0
  auto entry = std::ranges::find_if(
9904
0
      params, [&key](const auto& param) { return param.first == key; });
9905
0
  return entry != params.end();
9906
0
}
9907
9908
inline bool url_search_params::has(std::string_view key,
9909
0
                                   std::string_view value) noexcept {
9910
0
  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
9911
0
    return param.first == key && param.second == value;
9912
0
  });
9913
0
  return entry != params.end();
9914
0
}
9915
9916
0
inline std::string url_search_params::to_string() const {
9917
0
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9918
0
  std::string out{};
9919
0
  for (size_t i = 0; i < params.size(); i++) {
9920
0
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9921
0
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9922
9923
    // Performance optimization: Move this inside percent_encode.
9924
0
    std::ranges::replace(key, ' ', '+');
9925
0
    std::ranges::replace(value, ' ', '+');
9926
9927
0
    if (i != 0) {
9928
0
      out += "&";
9929
0
    }
9930
0
    out.append(key);
9931
0
    out += "=";
9932
0
    out.append(value);
9933
0
  }
9934
0
  return out;
9935
0
}
9936
9937
inline void url_search_params::set(const std::string_view key,
9938
0
                                   const std::string_view value) {
9939
0
  const auto find = [&key](const auto& param) { return param.first == key; };
9940
9941
0
  auto it = std::ranges::find_if(params, find);
9942
9943
0
  if (it == params.end()) {
9944
0
    params.emplace_back(key, value);
9945
0
  } else {
9946
0
    it->second = value;
9947
0
    params.erase(std::remove_if(std::next(it), params.end(), find),
9948
0
                 params.end());
9949
0
  }
9950
0
}
9951
9952
0
inline void url_search_params::remove(const std::string_view key) {
9953
0
  std::erase_if(params,
9954
0
                [&key](const auto& param) { return param.first == key; });
9955
0
}
9956
9957
inline void url_search_params::remove(const std::string_view key,
9958
0
                                      const std::string_view value) {
9959
0
  std::erase_if(params, [&key, &value](const auto& param) {
9960
0
    return param.first == key && param.second == value;
9961
0
  });
9962
0
}
9963
9964
0
inline void url_search_params::sort() {
9965
  // Keys are expected to be valid UTF-8, but percent_decode can produce
9966
  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
9967
0
  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
9968
0
                                      const key_value_pair& rhs) {
9969
0
    size_t i = 0, j = 0;
9970
0
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9971
0
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9972
0
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9973
0
      uint32_t codePoint1 = 0, codePoint2 = 0;
9974
9975
0
      if (low_surrogate1 != 0) {
9976
0
        codePoint1 = low_surrogate1;
9977
0
        low_surrogate1 = 0;
9978
0
      } else {
9979
0
        uint8_t c1 = uint8_t(lhs.first[i]);
9980
0
        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
9981
0
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9982
0
          i += 2;
9983
0
        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
9984
0
          codePoint1 = ((c1 & 0x0F) << 12) |
9985
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9986
0
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9987
0
          i += 3;
9988
0
        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
9989
0
          codePoint1 = ((c1 & 0x07) << 18) |
9990
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9991
0
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9992
0
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9993
0
          i += 4;
9994
9995
0
          codePoint1 -= 0x10000;
9996
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9997
0
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9998
0
          codePoint1 = high_surrogate;
9999
0
        } else {
10000
          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10001
0
          codePoint1 = c1;
10002
0
          i++;
10003
0
        }
10004
0
      }
10005
10006
0
      if (low_surrogate2 != 0) {
10007
0
        codePoint2 = low_surrogate2;
10008
0
        low_surrogate2 = 0;
10009
0
      } else {
10010
0
        uint8_t c2 = uint8_t(rhs.first[j]);
10011
0
        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
10012
0
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
10013
0
          j += 2;
10014
0
        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
10015
0
          codePoint2 = ((c2 & 0x0F) << 12) |
10016
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
10017
0
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
10018
0
          j += 3;
10019
0
        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
10020
0
          codePoint2 = ((c2 & 0x07) << 18) |
10021
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
10022
0
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
10023
0
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
10024
0
          j += 4;
10025
0
          codePoint2 -= 0x10000;
10026
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
10027
0
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
10028
0
          codePoint2 = high_surrogate;
10029
0
        } else {
10030
          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10031
0
          codePoint2 = c2;
10032
0
          j++;
10033
0
        }
10034
0
      }
10035
10036
0
      if (codePoint1 != codePoint2) {
10037
0
        return (codePoint1 < codePoint2);
10038
0
      }
10039
0
    }
10040
0
    return (j < rhs.first.size() || low_surrogate2 != 0);
10041
0
  });
10042
0
}
10043
10044
0
inline url_search_params_keys_iter url_search_params::get_keys() {
10045
0
  return url_search_params_keys_iter(*this);
10046
0
}
10047
10048
/**
10049
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
10050
 */
10051
0
inline url_search_params_values_iter url_search_params::get_values() {
10052
0
  return url_search_params_values_iter(*this);
10053
0
}
10054
10055
/**
10056
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
10057
 */
10058
0
inline url_search_params_entries_iter url_search_params::get_entries() {
10059
0
  return url_search_params_entries_iter(*this);
10060
0
}
10061
10062
template <typename T, url_search_params_iter_type Type>
10063
0
inline bool url_search_params_iter<T, Type>::has_next() const {
10064
0
  return pos < params.params.size();
10065
0
}
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::has_next() const
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::has_next() const
Unexecuted instantiation: ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::has_next() const
10066
10067
template <>
10068
0
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
10069
0
  if (!has_next()) {
10070
0
    return std::nullopt;
10071
0
  }
10072
0
  return params.params[pos++].first;
10073
0
}
10074
10075
template <>
10076
0
inline std::optional<std::string_view> url_search_params_values_iter::next() {
10077
0
  if (!has_next()) {
10078
0
    return std::nullopt;
10079
0
  }
10080
0
  return params.params[pos++].second;
10081
0
}
10082
10083
template <>
10084
inline std::optional<key_value_view_pair>
10085
0
url_search_params_entries_iter::next() {
10086
0
  if (!has_next()) {
10087
0
    return std::nullopt;
10088
0
  }
10089
0
  return params.params[pos++];
10090
0
}
10091
10092
}  // namespace ada
10093
10094
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
10095
/* end file include/ada/url_search_params-inl.h */
10096
10097
/* begin file include/ada/url_pattern-inl.h */
10098
/**
10099
 * @file url_pattern-inl.h
10100
 * @brief Declaration for the URLPattern inline functions.
10101
 */
10102
#ifndef ADA_URL_PATTERN_INL_H
10103
#define ADA_URL_PATTERN_INL_H
10104
10105
10106
#include <algorithm>
10107
#include <cstddef>
10108
#include <optional>
10109
#include <string>
10110
#include <string_view>
10111
#include <utility>
10112
#include <variant>
10113
#include <vector>
10114
10115
#if ADA_INCLUDE_URL_PATTERN
10116
namespace ada {
10117
10118
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
10119
0
  return protocol == other.protocol && username == other.username &&
10120
0
         password == other.password && hostname == other.hostname &&
10121
0
         port == other.port && search == other.search && hash == other.hash &&
10122
0
         pathname == other.pathname;
10123
0
}
10124
10125
inline bool url_pattern_component_result::operator==(
10126
0
    const url_pattern_component_result& other) const {
10127
0
  return input == other.input && groups == other.groups;
10128
0
}
10129
10130
template <url_pattern_regex::regex_concept regex_provider>
10131
url_pattern_component_result
10132
url_pattern_component<regex_provider>::create_component_match_result(
10133
    std::string&& input,
10134
    std::vector<std::optional<std::string>>&& exec_result) {
10135
  // Let result be a new URLPatternComponentResult.
10136
  // Set result["input"] to input.
10137
  // Let groups be a record<USVString, (USVString or undefined)>.
10138
  auto result =
10139
      url_pattern_component_result{.input = std::move(input), .groups = {}};
10140
10141
  // We explicitly start iterating from 0 even though the spec
10142
  // says we should start from 1. This case is handled by the
10143
  // std_regex_provider which removes the full match from index 0.
10144
  // Use min() to guard against potential mismatches between
10145
  // exec_result size and group_name_list size.
10146
  const size_t size = std::min(exec_result.size(), group_name_list.size());
10147
  result.groups.reserve(size);
10148
  for (size_t index = 0; index < size; index++) {
10149
    result.groups.emplace(group_name_list[index],
10150
                          std::move(exec_result[index]));
10151
  }
10152
  return result;
10153
}
10154
10155
template <url_pattern_regex::regex_concept regex_provider>
10156
std::string_view url_pattern<regex_provider>::get_protocol() const
10157
    ada_lifetime_bound {
10158
  // Return this's associated URL pattern's protocol component's pattern string.
10159
  return protocol_component.pattern;
10160
}
10161
template <url_pattern_regex::regex_concept regex_provider>
10162
std::string_view url_pattern<regex_provider>::get_username() const
10163
    ada_lifetime_bound {
10164
  // Return this's associated URL pattern's username component's pattern string.
10165
  return username_component.pattern;
10166
}
10167
template <url_pattern_regex::regex_concept regex_provider>
10168
std::string_view url_pattern<regex_provider>::get_password() const
10169
    ada_lifetime_bound {
10170
  // Return this's associated URL pattern's password component's pattern string.
10171
  return password_component.pattern;
10172
}
10173
template <url_pattern_regex::regex_concept regex_provider>
10174
std::string_view url_pattern<regex_provider>::get_hostname() const
10175
    ada_lifetime_bound {
10176
  // Return this's associated URL pattern's hostname component's pattern string.
10177
  return hostname_component.pattern;
10178
}
10179
template <url_pattern_regex::regex_concept regex_provider>
10180
std::string_view url_pattern<regex_provider>::get_port() const
10181
    ada_lifetime_bound {
10182
  // Return this's associated URL pattern's port component's pattern string.
10183
  return port_component.pattern;
10184
}
10185
template <url_pattern_regex::regex_concept regex_provider>
10186
std::string_view url_pattern<regex_provider>::get_pathname() const
10187
    ada_lifetime_bound {
10188
  // Return this's associated URL pattern's pathname component's pattern string.
10189
  return pathname_component.pattern;
10190
}
10191
template <url_pattern_regex::regex_concept regex_provider>
10192
std::string_view url_pattern<regex_provider>::get_search() const
10193
    ada_lifetime_bound {
10194
  // Return this's associated URL pattern's search component's pattern string.
10195
  return search_component.pattern;
10196
}
10197
template <url_pattern_regex::regex_concept regex_provider>
10198
std::string_view url_pattern<regex_provider>::get_hash() const
10199
    ada_lifetime_bound {
10200
  // Return this's associated URL pattern's hash component's pattern string.
10201
  return hash_component.pattern;
10202
}
10203
template <url_pattern_regex::regex_concept regex_provider>
10204
bool url_pattern<regex_provider>::ignore_case() const {
10205
  return ignore_case_;
10206
}
10207
template <url_pattern_regex::regex_concept regex_provider>
10208
bool url_pattern<regex_provider>::has_regexp_groups() const {
10209
  // If this's associated URL pattern's has regexp groups, then return true.
10210
  return protocol_component.has_regexp_groups ||
10211
         username_component.has_regexp_groups ||
10212
         password_component.has_regexp_groups ||
10213
         hostname_component.has_regexp_groups ||
10214
         port_component.has_regexp_groups ||
10215
         pathname_component.has_regexp_groups ||
10216
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
10217
}
10218
10219
0
inline bool url_pattern_part::is_regexp() const noexcept {
10220
0
  return type == url_pattern_part_type::REGEXP;
10221
0
}
10222
10223
inline std::string_view url_pattern_compile_component_options::get_delimiter()
10224
0
    const {
10225
0
  if (delimiter) {
10226
0
    return {&delimiter.value(), 1};
10227
0
  }
10228
0
  return {};
10229
0
}
10230
10231
inline std::string_view url_pattern_compile_component_options::get_prefix()
10232
0
    const {
10233
0
  if (prefix) {
10234
0
    return {&prefix.value(), 1};
10235
0
  }
10236
0
  return {};
10237
0
}
10238
10239
template <url_pattern_regex::regex_concept regex_provider>
10240
template <url_pattern_encoding_callback F>
10241
tl::expected<url_pattern_component<regex_provider>, errors>
10242
url_pattern_component<regex_provider>::compile(
10243
    std::string_view input, F& encoding_callback,
10244
    url_pattern_compile_component_options& options) {
10245
  ada_log("url_pattern_component::compile input: ", input);
10246
  // Let part list be the result of running parse a pattern string given input,
10247
  // options, and encoding callback.
10248
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
10249
                                                             encoding_callback);
10250
10251
  if (!part_list) {
10252
    ada_log("parse_pattern_string failed");
10253
    return tl::unexpected(part_list.error());
10254
  }
10255
10256
  // Detect pattern type early to potentially skip expensive regex compilation
10257
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
10258
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
10259
10260
  url_pattern_component_type component_type =
10261
      url_pattern_component_type::REGEXP;
10262
  std::string exact_match_value{};
10263
10264
  if (part_list->empty()) {
10265
    component_type = url_pattern_component_type::EMPTY;
10266
  } else if (part_list->size() == 1) {
10267
    const auto& part = (*part_list)[0];
10268
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
10269
        part.modifier == url_pattern_part_modifier::none &&
10270
        !options.ignore_case) {
10271
      component_type = url_pattern_component_type::EXACT_MATCH;
10272
      exact_match_value = part.value;
10273
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
10274
               part.modifier == url_pattern_part_modifier::none &&
10275
               part.prefix.empty() && part.suffix.empty()) {
10276
      component_type = url_pattern_component_type::FULL_WILDCARD;
10277
    }
10278
  }
10279
10280
  // For simple patterns, skip regex generation and compilation entirely
10281
  if (component_type != url_pattern_component_type::REGEXP) {
10282
    auto pattern_string =
10283
        url_pattern_helpers::generate_pattern_string(*part_list, options);
10284
    // For FULL_WILDCARD, we need the group name from
10285
    // generate_regular_expression
10286
    std::vector<std::string> name_list;
10287
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
10288
        !part_list->empty()) {
10289
      name_list.push_back((*part_list)[0].name);
10290
    }
10291
    return url_pattern_component<regex_provider>(
10292
        std::move(pattern_string), typename regex_provider::regex_type{},
10293
        std::move(name_list), has_regexp_groups, component_type,
10294
        std::move(exact_match_value));
10295
  }
10296
10297
  // Generate regex for complex patterns
10298
  auto [regular_expression_string, name_list] =
10299
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
10300
                                                                     options);
10301
  auto pattern_string =
10302
      url_pattern_helpers::generate_pattern_string(*part_list, options);
10303
10304
  std::optional<typename regex_provider::regex_type> regular_expression =
10305
      regex_provider::create_instance(regular_expression_string,
10306
                                      options.ignore_case);
10307
  if (!regular_expression) {
10308
    return tl::unexpected(errors::type_error);
10309
  }
10310
10311
  return url_pattern_component<regex_provider>(
10312
      std::move(pattern_string), std::move(*regular_expression),
10313
      std::move(name_list), has_regexp_groups, component_type,
10314
      std::move(exact_match_value));
10315
}
10316
10317
template <url_pattern_regex::regex_concept regex_provider>
10318
bool url_pattern_component<regex_provider>::fast_test(
10319
    std::string_view input) const {
10320
  // Fast path for simple patterns - avoid regex evaluation
10321
  // Using if-else for better branch prediction on common cases
10322
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10323
    return true;
10324
  }
10325
  if (type == url_pattern_component_type::EXACT_MATCH) {
10326
    return input == exact_match_value;
10327
  }
10328
  if (type == url_pattern_component_type::EMPTY) {
10329
    return input.empty();
10330
  }
10331
  // type == REGEXP
10332
  return regex_provider::regex_match(input, regexp);
10333
}
10334
10335
template <url_pattern_regex::regex_concept regex_provider>
10336
std::optional<std::vector<std::optional<std::string>>>
10337
url_pattern_component<regex_provider>::fast_match(
10338
    std::string_view input) const {
10339
  // Handle each type directly without redundant checks
10340
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10341
    // FULL_WILDCARD always matches - capture the input (even if empty)
10342
    // If there's no group name, return empty groups
10343
    if (group_name_list.empty()) {
10344
      return std::vector<std::optional<std::string>>{};
10345
    }
10346
    // Capture the matched input (including empty strings)
10347
    return std::vector<std::optional<std::string>>{std::string(input)};
10348
  }
10349
  if (type == url_pattern_component_type::EXACT_MATCH) {
10350
    if (input == exact_match_value) {
10351
      return std::vector<std::optional<std::string>>{};
10352
    }
10353
    return std::nullopt;
10354
  }
10355
  if (type == url_pattern_component_type::EMPTY) {
10356
    if (input.empty()) {
10357
      return std::vector<std::optional<std::string>>{};
10358
    }
10359
    return std::nullopt;
10360
  }
10361
  // type == REGEXP - use regex
10362
  return regex_provider::regex_search(input, regexp);
10363
}
10364
10365
template <url_pattern_regex::regex_concept regex_provider>
10366
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::exec(
10367
    const url_pattern_input& input, const std::string_view* base_url) {
10368
  // Return the result of match given this's associated URL pattern, input, and
10369
  // baseURL if given.
10370
  return match(input, base_url);
10371
}
10372
10373
template <url_pattern_regex::regex_concept regex_provider>
10374
bool url_pattern<regex_provider>::test_components(
10375
    std::string_view protocol, std::string_view username,
10376
    std::string_view password, std::string_view hostname, std::string_view port,
10377
    std::string_view pathname, std::string_view search,
10378
    std::string_view hash) const {
10379
  return protocol_component.fast_test(protocol) &&
10380
         username_component.fast_test(username) &&
10381
         password_component.fast_test(password) &&
10382
         hostname_component.fast_test(hostname) &&
10383
         port_component.fast_test(port) &&
10384
         pathname_component.fast_test(pathname) &&
10385
         search_component.fast_test(search) && hash_component.fast_test(hash);
10386
}
10387
10388
template <url_pattern_regex::regex_concept regex_provider>
10389
result<bool> url_pattern<regex_provider>::test(
10390
    const url_pattern_input& input, const std::string_view* base_url_string) {
10391
  // If input is a URLPatternInit
10392
  if (std::holds_alternative<url_pattern_init>(input)) {
10393
    if (base_url_string) {
10394
      return tl::unexpected(errors::type_error);
10395
    }
10396
10397
    std::string protocol{}, username{}, password{}, hostname{};
10398
    std::string port{}, pathname{}, search{}, hash{};
10399
10400
    auto apply_result = url_pattern_init::process(
10401
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10402
        protocol, username, password, hostname, port, pathname, search, hash);
10403
10404
    if (!apply_result) {
10405
      return false;
10406
    }
10407
10408
    std::string_view search_view = *apply_result->search;
10409
    if (search_view.starts_with("?")) {
10410
      search_view.remove_prefix(1);
10411
    }
10412
10413
    return test_components(*apply_result->protocol, *apply_result->username,
10414
                           *apply_result->password, *apply_result->hostname,
10415
                           *apply_result->port, *apply_result->pathname,
10416
                           search_view, *apply_result->hash);
10417
  }
10418
10419
  // URL string input path.
10420
  // Let baseURL be null. A default-constructed result holds a value (an
10421
  // empty URL), and a relative input would then resolve against it. Start
10422
  // from an error so "no base given" means no value.
10423
  result<url_aggregator> base_url = tl::unexpected(errors::type_error);
10424
  if (base_url_string) {
10425
    base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10426
    if (!base_url) {
10427
      return false;
10428
    }
10429
  }
10430
10431
  auto url =
10432
      ada::parse<url_aggregator>(std::get<std::string_view>(input),
10433
                                 base_url.has_value() ? &*base_url : nullptr);
10434
  if (!url) {
10435
    return false;
10436
  }
10437
10438
  // Extract components as string_view
10439
  auto protocol_view = url->get_protocol();
10440
  if (protocol_view.ends_with(":")) {
10441
    protocol_view.remove_suffix(1);
10442
  }
10443
10444
  auto search_view = url->get_search();
10445
  if (search_view.starts_with("?")) {
10446
    search_view.remove_prefix(1);
10447
  }
10448
10449
  auto hash_view = url->get_hash();
10450
  if (hash_view.starts_with("#")) {
10451
    hash_view.remove_prefix(1);
10452
  }
10453
10454
  return test_components(protocol_view, url->get_username(),
10455
                         url->get_password(), url->get_hostname(),
10456
                         url->get_port(), url->get_pathname(), search_view,
10457
                         hash_view);
10458
}
10459
10460
template <url_pattern_regex::regex_concept regex_provider>
10461
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::match(
10462
    const url_pattern_input& input, const std::string_view* base_url_string) {
10463
  std::string protocol{};
10464
  std::string username{};
10465
  std::string password{};
10466
  std::string hostname{};
10467
  std::string port{};
10468
  std::string pathname{};
10469
  std::string search{};
10470
  std::string hash{};
10471
10472
  // Let inputs be an empty list.
10473
  // Append input to inputs.
10474
  std::vector inputs{input};
10475
10476
  // If input is a URLPatternInit then:
10477
  if (std::holds_alternative<url_pattern_init>(input)) {
10478
    ada_log(
10479
        "url_pattern::match called with url_pattern_init and base_url_string=",
10480
        base_url_string);
10481
    // If baseURLString was given, throw a TypeError.
10482
    if (base_url_string) {
10483
      ada_log("failed to match because base_url_string was given");
10484
      return tl::unexpected(errors::type_error);
10485
    }
10486
10487
    // Let applyResult be the result of process a URLPatternInit given input,
10488
    // "url", protocol, username, password, hostname, port, pathname, search,
10489
    // and hash.
10490
    auto apply_result = url_pattern_init::process(
10491
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10492
        protocol, username, password, hostname, port, pathname, search, hash);
10493
10494
    // If this throws an exception, catch it, and return null.
10495
    if (!apply_result.has_value()) {
10496
      ada_log("match returned std::nullopt because process threw");
10497
      return std::nullopt;
10498
    }
10499
10500
    // Set protocol to applyResult["protocol"].
10501
    ADA_ASSERT_TRUE(apply_result->protocol.has_value());
10502
    protocol = std::move(apply_result->protocol.value());
10503
10504
    // Set username to applyResult["username"].
10505
    ADA_ASSERT_TRUE(apply_result->username.has_value());
10506
    username = std::move(apply_result->username.value());
10507
10508
    // Set password to applyResult["password"].
10509
    ADA_ASSERT_TRUE(apply_result->password.has_value());
10510
    password = std::move(apply_result->password.value());
10511
10512
    // Set hostname to applyResult["hostname"].
10513
    ADA_ASSERT_TRUE(apply_result->hostname.has_value());
10514
    hostname = std::move(apply_result->hostname.value());
10515
10516
    // Set port to applyResult["port"].
10517
    ADA_ASSERT_TRUE(apply_result->port.has_value());
10518
    port = std::move(apply_result->port.value());
10519
10520
    // Set pathname to applyResult["pathname"].
10521
    ADA_ASSERT_TRUE(apply_result->pathname.has_value());
10522
    pathname = std::move(apply_result->pathname.value());
10523
10524
    // Set search to applyResult["search"].
10525
    ADA_ASSERT_TRUE(apply_result->search.has_value());
10526
    if (apply_result->search->starts_with("?")) {
10527
      search = apply_result->search->substr(1);
10528
    } else {
10529
      search = std::move(apply_result->search.value());
10530
    }
10531
10532
    // Set hash to applyResult["hash"].
10533
    ADA_ASSERT_TRUE(apply_result->hash.has_value());
10534
    ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#"));
10535
    hash = std::move(apply_result->hash.value());
10536
  } else {
10537
    ADA_ASSERT_TRUE(std::holds_alternative<std::string_view>(input));
10538
10539
    // Let baseURL be null. A default-constructed result holds a value (an
10540
    // empty URL), and a relative input would then resolve against it. Start
10541
    // from an error so "no base given" means no value.
10542
    result<url_aggregator> base_url = tl::unexpected(errors::type_error);
10543
10544
    // If baseURLString was given, then:
10545
    if (base_url_string) {
10546
      // Let baseURL be the result of parsing baseURLString.
10547
      base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10548
10549
      // If baseURL is failure, return null.
10550
      if (!base_url) {
10551
        ada_log("match returned std::nullopt because failed to parse base_url=",
10552
                *base_url_string);
10553
        return std::nullopt;
10554
      }
10555
10556
      // Append baseURLString to inputs.
10557
      inputs.emplace_back(*base_url_string);
10558
    }
10559
10560
    url_aggregator* base_url_value =
10561
        base_url.has_value() ? &*base_url : nullptr;
10562
10563
    // Set url to the result of parsing input given baseURL.
10564
    auto url = ada::parse<url_aggregator>(std::get<std::string_view>(input),
10565
                                          base_url_value);
10566
10567
    // If url is failure, return null.
10568
    if (!url) {
10569
      ada_log("match returned std::nullopt because url failed");
10570
      return std::nullopt;
10571
    }
10572
10573
    // Set protocol to url's scheme.
10574
    // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':'
10575
    // is removed. Similar work was done on workerd:
10576
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038
10577
    protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1);
10578
    // Set username to url's username.
10579
    username = url->get_username();
10580
    // Set password to url's password.
10581
    password = url->get_password();
10582
    // Set hostname to url's host, serialized, or the empty string if the value
10583
    // is null.
10584
    hostname = url->get_hostname();
10585
    // Set port to url's port, serialized, or the empty string if the value is
10586
    // null.
10587
    port = url->get_port();
10588
    // Set pathname to the result of URL path serializing url.
10589
    pathname = url->get_pathname();
10590
    // Set search to url's query or the empty string if the value is null.
10591
    // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?'
10592
    // is removed. Similar work was done on workerd:
10593
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232
10594
    if (url->has_search()) {
10595
      auto view = url->get_search();
10596
      search = view.starts_with("?") ? url->get_search().substr(1) : view;
10597
    }
10598
    // Set hash to url's fragment or the empty string if the value is null.
10599
    // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is
10600
    // removed. Similar work was done on workerd:
10601
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242
10602
    if (url->has_hash()) {
10603
      auto view = url->get_hash();
10604
      hash = view.starts_with("#") ? url->get_hash().substr(1) : view;
10605
    }
10606
  }
10607
10608
  // Use fast_match which skips regex for simple patterns (EMPTY, EXACT_MATCH,
10609
  // FULL_WILDCARD) and only falls back to regex for complex REGEXP patterns.
10610
10611
  // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol
10612
  // component's regular expression, protocol).
10613
  auto protocol_exec_result = protocol_component.fast_match(protocol);
10614
  if (!protocol_exec_result) {
10615
    return std::nullopt;
10616
  }
10617
10618
  // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username
10619
  // component's regular expression, username).
10620
  auto username_exec_result = username_component.fast_match(username);
10621
  if (!username_exec_result) {
10622
    return std::nullopt;
10623
  }
10624
10625
  // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password
10626
  // component's regular expression, password).
10627
  auto password_exec_result = password_component.fast_match(password);
10628
  if (!password_exec_result) {
10629
    return std::nullopt;
10630
  }
10631
10632
  // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname
10633
  // component's regular expression, hostname).
10634
  auto hostname_exec_result = hostname_component.fast_match(hostname);
10635
  if (!hostname_exec_result) {
10636
    return std::nullopt;
10637
  }
10638
10639
  // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's
10640
  // regular expression, port).
10641
  auto port_exec_result = port_component.fast_match(port);
10642
  if (!port_exec_result) {
10643
    return std::nullopt;
10644
  }
10645
10646
  // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname
10647
  // component's regular expression, pathname).
10648
  auto pathname_exec_result = pathname_component.fast_match(pathname);
10649
  if (!pathname_exec_result) {
10650
    return std::nullopt;
10651
  }
10652
10653
  // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's
10654
  // regular expression, search).
10655
  auto search_exec_result = search_component.fast_match(search);
10656
  if (!search_exec_result) {
10657
    return std::nullopt;
10658
  }
10659
10660
  // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's
10661
  // regular expression, hash).
10662
  auto hash_exec_result = hash_component.fast_match(hash);
10663
  if (!hash_exec_result) {
10664
    return std::nullopt;
10665
  }
10666
10667
  // Let result be a new URLPatternResult.
10668
  auto result = url_pattern_result{};
10669
  // Set result["inputs"] to inputs.
10670
  result.inputs = std::move(inputs);
10671
  // Set result["protocol"] to the result of creating a component match result
10672
  // given urlPattern's protocol component, protocol, and protocolExecResult.
10673
  result.protocol = protocol_component.create_component_match_result(
10674
      std::move(protocol), std::move(*protocol_exec_result));
10675
10676
  // Set result["username"] to the result of creating a component match result
10677
  // given urlPattern's username component, username, and usernameExecResult.
10678
  result.username = username_component.create_component_match_result(
10679
      std::move(username), std::move(*username_exec_result));
10680
10681
  // Set result["password"] to the result of creating a component match result
10682
  // given urlPattern's password component, password, and passwordExecResult.
10683
  result.password = password_component.create_component_match_result(
10684
      std::move(password), std::move(*password_exec_result));
10685
10686
  // Set result["hostname"] to the result of creating a component match result
10687
  // given urlPattern's hostname component, hostname, and hostnameExecResult.
10688
  result.hostname = hostname_component.create_component_match_result(
10689
      std::move(hostname), std::move(*hostname_exec_result));
10690
10691
  // Set result["port"] to the result of creating a component match result given
10692
  // urlPattern's port component, port, and portExecResult.
10693
  result.port = port_component.create_component_match_result(
10694
      std::move(port), std::move(*port_exec_result));
10695
10696
  // Set result["pathname"] to the result of creating a component match result
10697
  // given urlPattern's pathname component, pathname, and pathnameExecResult.
10698
  result.pathname = pathname_component.create_component_match_result(
10699
      std::move(pathname), std::move(*pathname_exec_result));
10700
10701
  // Set result["search"] to the result of creating a component match result
10702
  // given urlPattern's search component, search, and searchExecResult.
10703
  result.search = search_component.create_component_match_result(
10704
      std::move(search), std::move(*search_exec_result));
10705
10706
  // Set result["hash"] to the result of creating a component match result given
10707
  // urlPattern's hash component, hash, and hashExecResult.
10708
  result.hash = hash_component.create_component_match_result(
10709
      std::move(hash), std::move(*hash_exec_result));
10710
10711
  return result;
10712
}
10713
10714
}  // namespace ada
10715
#endif  // ADA_INCLUDE_URL_PATTERN
10716
#endif
10717
/* end file include/ada/url_pattern-inl.h */
10718
/* begin file include/ada/url_pattern_helpers-inl.h */
10719
/**
10720
 * @file url_pattern_helpers-inl.h
10721
 * @brief Declaration for the URLPattern helpers.
10722
 */
10723
#ifndef ADA_URL_PATTERN_HELPERS_INL_H
10724
#define ADA_URL_PATTERN_HELPERS_INL_H
10725
10726
#include <cstddef>
10727
#include <cstdint>
10728
#include <optional>
10729
#include <string>
10730
#include <string_view>
10731
#include <utility>
10732
#include <vector>
10733
10734
10735
#if ADA_INCLUDE_URL_PATTERN
10736
namespace ada::url_pattern_helpers {
10737
#if defined(ADA_TESTING) || defined(ADA_LOGGING)
10738
0
inline std::string to_string(token_type type) {
10739
0
  switch (type) {
10740
0
    case token_type::INVALID_CHAR:
10741
0
      return "INVALID_CHAR";
10742
0
    case token_type::OPEN:
10743
0
      return "OPEN";
10744
0
    case token_type::CLOSE:
10745
0
      return "CLOSE";
10746
0
    case token_type::REGEXP:
10747
0
      return "REGEXP";
10748
0
    case token_type::NAME:
10749
0
      return "NAME";
10750
0
    case token_type::CHAR:
10751
0
      return "CHAR";
10752
0
    case token_type::ESCAPED_CHAR:
10753
0
      return "ESCAPED_CHAR";
10754
0
    case token_type::OTHER_MODIFIER:
10755
0
      return "OTHER_MODIFIER";
10756
0
    case token_type::ASTERISK:
10757
0
      return "ASTERISK";
10758
0
    case token_type::END:
10759
0
      return "END";
10760
0
    default:
10761
0
      ada::unreachable();
10762
0
  }
10763
0
}
10764
#endif  // defined(ADA_TESTING) || defined(ADA_LOGGING)
10765
10766
template <url_pattern_regex::regex_concept regex_provider>
10767
constexpr void constructor_string_parser<regex_provider>::rewind() {
10768
  // Set parser's token index to parser's component start.
10769
  token_index = component_start;
10770
  // Set parser's token increment to 0.
10771
  token_increment = 0;
10772
}
10773
10774
template <url_pattern_regex::regex_concept regex_provider>
10775
constexpr bool constructor_string_parser<regex_provider>::is_hash_prefix() {
10776
  // Return the result of running is a non-special pattern char given parser,
10777
  // parser's token index and "#".
10778
  return is_non_special_pattern_char(token_index, '#');
10779
}
10780
10781
template <url_pattern_regex::regex_concept regex_provider>
10782
constexpr bool constructor_string_parser<regex_provider>::is_search_prefix() {
10783
  // If result of running is a non-special pattern char given parser, parser's
10784
  // token index and "?" is true, then return true.
10785
  if (is_non_special_pattern_char(token_index, '?')) {
10786
    return true;
10787
  }
10788
10789
  // If parser's token list[parser's token index]'s value is not "?", then
10790
  // return false.
10791
  if (token_list[token_index].value != "?") {
10792
    return false;
10793
  }
10794
10795
  // If previous index is less than 0, then return true.
10796
  if (token_index == 0) return true;
10797
  // Let previous index be parser's token index - 1.
10798
  auto previous_index = token_index - 1;
10799
  // Let previous token be the result of running get a safe token given parser
10800
  // and previous index.
10801
  auto previous_token = get_safe_token(previous_index);
10802
  ADA_ASSERT_TRUE(previous_token);
10803
  // If any of the following are true, then return false:
10804
  // - previous token's type is "name".
10805
  // - previous token's type is "regexp".
10806
  // - previous token's type is "close".
10807
  // - previous token's type is "asterisk".
10808
  return !(previous_token->type == token_type::NAME ||
10809
           previous_token->type == token_type::REGEXP ||
10810
           previous_token->type == token_type::CLOSE ||
10811
           previous_token->type == token_type::ASTERISK);
10812
}
10813
10814
template <url_pattern_regex::regex_concept regex_provider>
10815
constexpr bool
10816
constructor_string_parser<regex_provider>::is_non_special_pattern_char(
10817
    size_t index, uint32_t value) const {
10818
  // Let token be the result of running get a safe token given parser and index.
10819
  auto token = get_safe_token(index);
10820
  ADA_ASSERT_TRUE(token);
10821
10822
  // If token's value is not value, then return false.
10823
  // TODO: Remove this once we make sure get_safe_token returns a non-empty
10824
  // string.
10825
  if (!token->value.empty() &&
10826
      static_cast<uint32_t>(token->value[0]) != value) {
10827
    return false;
10828
  }
10829
10830
  // If any of the following are true:
10831
  // - token's type is "char";
10832
  // - token's type is "escaped-char"; or
10833
  // - token's type is "invalid-char",
10834
  // - then return true.
10835
  return token->type == token_type::CHAR ||
10836
         token->type == token_type::ESCAPED_CHAR ||
10837
         token->type == token_type::INVALID_CHAR;
10838
}
10839
10840
template <url_pattern_regex::regex_concept regex_provider>
10841
constexpr const token*
10842
constructor_string_parser<regex_provider>::get_safe_token(size_t index) const {
10843
  // If index is less than parser's token list's size, then return parser's
10844
  // token list[index].
10845
  if (index < token_list.size()) [[likely]] {
10846
    return &token_list[index];
10847
  }
10848
10849
  // Assert: parser's token list's size is greater than or equal to 1.
10850
  ADA_ASSERT_TRUE(!token_list.empty());
10851
10852
  // Let token be parser's token list[last index].
10853
  // Assert: token's type is "end".
10854
  ADA_ASSERT_TRUE(token_list.back().type == token_type::END);
10855
10856
  // Return token.
10857
  return &token_list.back();
10858
}
10859
10860
template <url_pattern_regex::regex_concept regex_provider>
10861
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10862
    const {
10863
  // If parser's token list[parser's token index]'s type is "open", then return
10864
  // true.
10865
  return token_list[token_index].type == token_type::OPEN;
10866
}
10867
10868
template <url_pattern_regex::regex_concept regex_provider>
10869
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10870
    const {
10871
  // If parser's token list[parser's token index]'s type is "close", then return
10872
  // true.
10873
  return token_list[token_index].type == token_type::CLOSE;
10874
}
10875
10876
template <url_pattern_regex::regex_concept regex_provider>
10877
constexpr bool
10878
constructor_string_parser<regex_provider>::next_is_authority_slashes() const {
10879
  // If the result of running is a non-special pattern char given parser,
10880
  // parser's token index + 1, and "/" is false, then return false.
10881
  if (!is_non_special_pattern_char(token_index + 1, '/')) {
10882
    return false;
10883
  }
10884
  // If the result of running is a non-special pattern char given parser,
10885
  // parser's token index + 2, and "/" is false, then return false.
10886
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10887
    return false;
10888
  }
10889
  return true;
10890
}
10891
10892
template <url_pattern_regex::regex_concept regex_provider>
10893
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10894
    const {
10895
  // Return the result of running is a non-special pattern char given parser,
10896
  // parser's token index, and ":".
10897
  return is_non_special_pattern_char(token_index, ':');
10898
}
10899
10900
template <url_pattern_regex::regex_concept regex_provider>
10901
void constructor_string_parser<regex_provider>::change_state(State new_state,
10902
                                                             size_t skip) {
10903
  // If parser's state is not "init", not "authority", and not "done", then set
10904
  // parser's result[parser's state] to the result of running make a component
10905
  // string given parser.
10906
  if (state != State::INIT && state != State::AUTHORITY &&
10907
      state != State::DONE) {
10908
    auto value = make_component_string();
10909
    // TODO: Simplify this.
10910
    switch (state) {
10911
      case State::PROTOCOL: {
10912
        result.protocol = value;
10913
        break;
10914
      }
10915
      case State::USERNAME: {
10916
        result.username = value;
10917
        break;
10918
      }
10919
      case State::PASSWORD: {
10920
        result.password = value;
10921
        break;
10922
      }
10923
      case State::HOSTNAME: {
10924
        result.hostname = value;
10925
        break;
10926
      }
10927
      case State::PORT: {
10928
        result.port = value;
10929
        break;
10930
      }
10931
      case State::PATHNAME: {
10932
        result.pathname = value;
10933
        break;
10934
      }
10935
      case State::SEARCH: {
10936
        result.search = value;
10937
        break;
10938
      }
10939
      case State::HASH: {
10940
        result.hash = value;
10941
        break;
10942
      }
10943
      default:
10944
        ada::unreachable();
10945
    }
10946
  }
10947
10948
  // If parser's state is not "init" and new state is not "done", then:
10949
  if (state != State::INIT && new_state != State::DONE) {
10950
    // If parser's state is "protocol", "authority", "username", or "password";
10951
    // new state is "port", "pathname", "search", or "hash"; and parser's
10952
    // result["hostname"] does not exist, then set parser's result["hostname"]
10953
    // to the empty string.
10954
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10955
         state == State::USERNAME || state == State::PASSWORD) &&
10956
        (new_state == State::PORT || new_state == State::PATHNAME ||
10957
         new_state == State::SEARCH || new_state == State::HASH) &&
10958
        !result.hostname)
10959
      result.hostname = "";
10960
  }
10961
10962
  // If parser's state is "protocol", "authority", "username", "password",
10963
  // "hostname", or "port"; new state is "search" or "hash"; and parser's
10964
  // result["pathname"] does not exist, then:
10965
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10966
       state == State::USERNAME || state == State::PASSWORD ||
10967
       state == State::HOSTNAME || state == State::PORT) &&
10968
      (new_state == State::SEARCH || new_state == State::HASH) &&
10969
      !result.pathname) {
10970
    if (protocol_matches_a_special_scheme_flag) {
10971
      result.pathname = "/";
10972
    } else {
10973
      // Otherwise, set parser's result["pathname"] to the empty string.
10974
      result.pathname = "";
10975
    }
10976
  }
10977
10978
  // If parser's state is "protocol", "authority", "username", "password",
10979
  // "hostname", "port", or "pathname"; new state is "hash"; and parser's
10980
  // result["search"] does not exist, then set parser's result["search"] to
10981
  // the empty string.
10982
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10983
       state == State::USERNAME || state == State::PASSWORD ||
10984
       state == State::HOSTNAME || state == State::PORT ||
10985
       state == State::PATHNAME) &&
10986
      new_state == State::HASH && !result.search) {
10987
    result.search = "";
10988
  }
10989
10990
  // Set parser's state to new state.
10991
  state = new_state;
10992
  // Increment parser's token index by skip.
10993
  token_index += skip;
10994
  // Set parser's component start to parser's token index.
10995
  component_start = token_index;
10996
  // Set parser's token increment to 0.
10997
  token_increment = 0;
10998
}
10999
11000
template <url_pattern_regex::regex_concept regex_provider>
11001
std::string constructor_string_parser<regex_provider>::make_component_string() {
11002
  // Assert: parser's token index is less than parser's token list's size.
11003
  ADA_ASSERT_TRUE(token_index < token_list.size());
11004
11005
  // Let token be parser's token list[parser's token index].
11006
  // Let end index be token's index.
11007
  const auto end_index = token_list[token_index].index;
11008
  // Let component start token be the result of running get a safe token given
11009
  // parser and parser's component start.
11010
  const auto component_start_token = get_safe_token(component_start);
11011
  ADA_ASSERT_TRUE(component_start_token);
11012
  // Let component start input index be component start token's index.
11013
  const auto component_start_input_index = component_start_token->index;
11014
  // Return the code point substring from component start input index to end
11015
  // index within parser's input.
11016
  return std::string(input.substr(component_start_input_index,
11017
                                  end_index - component_start_input_index));
11018
}
11019
11020
template <url_pattern_regex::regex_concept regex_provider>
11021
constexpr bool
11022
constructor_string_parser<regex_provider>::is_an_identity_terminator() const {
11023
  // Return the result of running is a non-special pattern char given parser,
11024
  // parser's token index, and "@".
11025
  return is_non_special_pattern_char(token_index, '@');
11026
}
11027
11028
template <url_pattern_regex::regex_concept regex_provider>
11029
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
11030
    const {
11031
  // Return the result of running is a non-special pattern char given parser,
11032
  // parser's token index, and "/".
11033
  return is_non_special_pattern_char(token_index, '/');
11034
}
11035
11036
template <url_pattern_regex::regex_concept regex_provider>
11037
constexpr bool constructor_string_parser<regex_provider>::is_password_prefix()
11038
    const {
11039
  // Return the result of running is a non-special pattern char given parser,
11040
  // parser's token index, and ":".
11041
  return is_non_special_pattern_char(token_index, ':');
11042
}
11043
11044
template <url_pattern_regex::regex_concept regex_provider>
11045
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_open()
11046
    const {
11047
  // Return the result of running is a non-special pattern char given parser,
11048
  // parser's token index, and "[".
11049
  return is_non_special_pattern_char(token_index, '[');
11050
}
11051
11052
template <url_pattern_regex::regex_concept regex_provider>
11053
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
11054
    const {
11055
  // Return the result of running is a non-special pattern char given parser,
11056
  // parser's token index, and "]".
11057
  return is_non_special_pattern_char(token_index, ']');
11058
}
11059
11060
template <url_pattern_regex::regex_concept regex_provider>
11061
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
11062
    const {
11063
  // Return the result of running is a non-special pattern char given parser,
11064
  // parser's token index, and ":".
11065
  return is_non_special_pattern_char(token_index, ':');
11066
}
11067
11068
0
constexpr void Tokenizer::get_next_code_point() {
11069
0
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
11070
0
  ADA_ASSERT_TRUE(next_index < input.size());
11071
  // Decode the next UTF-8 code point. If malformed or truncated, mark it as
11072
  // invalid, return the offending byte as the code point, and advance by one
11073
  // to guarantee forward progress.
11074
0
  invalid_code_point = false;
11075
0
  code_point = 0;
11076
0
  size_t number_bytes = 0;
11077
0
  const size_t initial_index = next_index;
11078
0
  unsigned char first_byte = input[next_index];
11079
11080
0
  if ((first_byte & 0x80) == 0) {
11081
    // 1-byte character (ASCII)
11082
0
    next_index++;
11083
0
    code_point = first_byte;
11084
0
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
11085
0
            uint32_t(code_point));
11086
0
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
11087
0
            " input.size()=", input.size());
11088
0
    return;
11089
0
  }
11090
0
  ada_log("Tokenizer::get_next_code_point read first byte=",
11091
0
          uint32_t(first_byte));
11092
0
  if ((first_byte & 0xE0) == 0xC0) {
11093
0
    code_point = first_byte & 0x1F;
11094
0
    number_bytes = 2;
11095
0
    ada_log("Tokenizer::get_next_code_point two bytes");
11096
0
  } else if ((first_byte & 0xF0) == 0xE0) {
11097
0
    code_point = first_byte & 0x0F;
11098
0
    number_bytes = 3;
11099
0
    ada_log("Tokenizer::get_next_code_point three bytes");
11100
0
  } else if ((first_byte & 0xF8) == 0xF0) {
11101
0
    code_point = first_byte & 0x07;
11102
0
    number_bytes = 4;
11103
0
    ada_log("Tokenizer::get_next_code_point four bytes");
11104
0
  }
11105
11106
  // Invalid leading bytes that still match a multi-byte prefix.
11107
0
  if ((number_bytes == 2 && first_byte < 0xC2) ||
11108
0
      (number_bytes == 4 && first_byte > 0xF4)) {
11109
0
    invalid_code_point = true;
11110
0
    code_point = first_byte;
11111
0
    next_index = initial_index + 1;
11112
0
    return;
11113
0
  }
11114
11115
  // Invalid leading byte (e.g., continuation byte outside a sequence).
11116
0
  if (number_bytes == 0) {
11117
0
    invalid_code_point = true;
11118
0
    code_point = first_byte;
11119
0
    next_index = initial_index + 1;
11120
0
    return;
11121
0
  }
11122
11123
  // Truncated UTF-8 sequence.
11124
0
  if (number_bytes + next_index > input.size()) {
11125
0
    invalid_code_point = true;
11126
0
    code_point = first_byte;
11127
0
    next_index = initial_index + 1;
11128
0
    return;
11129
0
  }
11130
11131
0
  for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) {
11132
0
    unsigned char byte = input[i];
11133
0
    if ((byte & 0xC0) != 0x80) {
11134
0
      invalid_code_point = true;
11135
0
      code_point = first_byte;
11136
0
      next_index = initial_index + 1;
11137
0
      return;
11138
0
    }
11139
0
    ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte));
11140
0
    code_point = (code_point << 6) | (byte & 0x3F);
11141
0
  }
11142
0
  ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=",
11143
0
          uint32_t(code_point));
11144
0
  ada_log("Tokenizer::get_next_code_point next_index =", next_index,
11145
0
          " input.size()=", input.size());
11146
0
  next_index += number_bytes;
11147
0
}
11148
11149
0
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
11150
0
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
11151
0
          new_index);
11152
  // Set tokenizer's next index to index.
11153
0
  next_index = new_index;
11154
  // Run get the next code point given tokenizer.
11155
0
  get_next_code_point();
11156
0
}
11157
11158
inline void Tokenizer::add_token(token_type type, size_t next_position,
11159
0
                                 size_t value_position, size_t value_length) {
11160
0
  ada_log("Tokenizer::add_token called with type=", to_string(type),
11161
0
          " next_position=", next_position, " value_position=", value_position);
11162
0
  ADA_ASSERT_TRUE(next_position >= value_position);
11163
11164
  // Let token be a new token.
11165
  // Set token's type to type.
11166
  // Set token's index to tokenizer's index.
11167
  // Set token's value to the code point substring from value position with
11168
  // length value length within tokenizer's input.
11169
  // Append token to the back of tokenizer's token list.
11170
0
  token_list.emplace_back(type, index,
11171
0
                          input.substr(value_position, value_length));
11172
  // Set tokenizer's index to next position.
11173
0
  index = next_position;
11174
0
}
11175
11176
inline void Tokenizer::add_token_with_default_length(token_type type,
11177
                                                     size_t next_position,
11178
0
                                                     size_t value_position) {
11179
  // Let computed length be next position - value position.
11180
0
  auto computed_length = next_position - value_position;
11181
  // Run add a token given tokenizer, type, next position, value position, and
11182
  // computed length.
11183
0
  add_token(type, next_position, value_position, computed_length);
11184
0
}
11185
11186
0
inline void Tokenizer::add_token_with_defaults(token_type type) {
11187
0
  ada_log("Tokenizer::add_token_with_defaults called with type=",
11188
0
          to_string(type));
11189
  // Run add a token with default length given tokenizer, type, tokenizer's next
11190
  // index, and tokenizer's index.
11191
0
  add_token_with_default_length(type, next_index, index);
11192
0
}
11193
11194
inline ada_warn_unused std::optional<errors>
11195
Tokenizer::process_tokenizing_error(size_t next_position,
11196
0
                                    size_t value_position) {
11197
  // If tokenizer's policy is "strict", then throw a TypeError.
11198
0
  if (policy == token_policy::strict) {
11199
0
    ada_log("process_tokenizing_error failed with next_position=",
11200
0
            next_position, " value_position=", value_position);
11201
0
    return errors::type_error;
11202
0
  }
11203
  // Assert: tokenizer's policy is "lenient".
11204
0
  ADA_ASSERT_TRUE(policy == token_policy::lenient);
11205
  // Run add a token with default length given tokenizer, "invalid-char", next
11206
  // position, and value position.
11207
0
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
11208
0
                                value_position);
11209
0
  return std::nullopt;
11210
0
}
11211
11212
template <url_pattern_encoding_callback F>
11213
0
token* url_pattern_parser<F>::try_consume_modifier_token() {
11214
  // Let token be the result of running try to consume a token given parser and
11215
  // "other-modifier".
11216
0
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
11217
  // If token is not null, then return token.
11218
0
  if (token) return token;
11219
  // Set token to the result of running try to consume a token given parser and
11220
  // "asterisk".
11221
  // Return token.
11222
0
  return try_consume_token(token_type::ASTERISK);
11223
0
}
11224
11225
template <url_pattern_encoding_callback F>
11226
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
11227
0
    const token* name_token) {
11228
  // Let token be the result of running try to consume a token given parser and
11229
  // "regexp".
11230
0
  auto token = try_consume_token(token_type::REGEXP);
11231
  // If name token is null and token is null, then set token to the result of
11232
  // running try to consume a token given parser and "asterisk".
11233
0
  if (!name_token && !token) {
11234
0
    token = try_consume_token(token_type::ASTERISK);
11235
0
  }
11236
  // Return token.
11237
0
  return token;
11238
0
}
11239
11240
template <url_pattern_encoding_callback F>
11241
0
token* url_pattern_parser<F>::try_consume_token(token_type type) {
11242
0
  ada_log("url_pattern_parser::try_consume_token called with type=",
11243
0
          to_string(type));
11244
  // Assert: parser's index is less than parser's token list size.
11245
0
  ADA_ASSERT_TRUE(index < tokens.size());
11246
  // Let next token be parser's token list[parser's index].
11247
0
  auto& next_token = tokens[index];
11248
  // If next token's type is not type return null.
11249
0
  if (next_token.type != type) return nullptr;
11250
  // Increase parser's index by 1.
11251
0
  index++;
11252
  // Return next token.
11253
0
  return &next_token;
11254
0
}
11255
11256
template <url_pattern_encoding_callback F>
11257
0
std::string url_pattern_parser<F>::consume_text() {
11258
  // Let result be the empty string.
11259
0
  std::string result{};
11260
  // While true:
11261
0
  while (true) {
11262
    // Let token be the result of running try to consume a token given parser
11263
    // and "char".
11264
0
    auto token = try_consume_token(token_type::CHAR);
11265
    // If token is null, then set token to the result of running try to consume
11266
    // a token given parser and "escaped-char".
11267
0
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
11268
    // If token is null, then break.
11269
0
    if (!token) break;
11270
    // Append token's value to the end of result.
11271
0
    result.append(token->value);
11272
0
  }
11273
  // Return result.
11274
0
  return result;
11275
0
}
11276
11277
template <url_pattern_encoding_callback F>
11278
0
bool url_pattern_parser<F>::consume_required_token(token_type type) {
11279
0
  ada_log("url_pattern_parser::consume_required_token called with type=",
11280
0
          to_string(type));
11281
  // Let result be the result of running try to consume a token given parser and
11282
  // type.
11283
0
  return try_consume_token(type) != nullptr;
11284
0
}
11285
11286
template <url_pattern_encoding_callback F>
11287
std::optional<errors>
11288
0
url_pattern_parser<F>::maybe_add_part_from_the_pending_fixed_value() {
11289
  // If parser's pending fixed value is the empty string, then return.
11290
0
  if (pending_fixed_value.empty()) {
11291
0
    ada_log("pending_fixed_value is empty");
11292
0
    return std::nullopt;
11293
0
  }
11294
  // Let encoded value be the result of running parser's encoding callback given
11295
  // parser's pending fixed value.
11296
0
  auto encoded_value = encoding_callback(pending_fixed_value);
11297
0
  if (!encoded_value) {
11298
0
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
11299
0
    return encoded_value.error();
11300
0
  }
11301
  // Set parser's pending fixed value to the empty string.
11302
0
  pending_fixed_value.clear();
11303
  // Let part be a new part whose type is "fixed-text", value is encoded value,
11304
  // and modifier is "none".
11305
  // Append part to parser's part list.
11306
0
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11307
0
                     std::move(*encoded_value),
11308
0
                     url_pattern_part_modifier::none);
11309
0
  return std::nullopt;
11310
0
}
11311
11312
template <url_pattern_encoding_callback F>
11313
std::optional<errors> url_pattern_parser<F>::add_part(
11314
    std::string_view prefix, token* name_token, token* regexp_or_wildcard_token,
11315
0
    std::string_view suffix, token* modifier_token) {
11316
  // Let modifier be "none".
11317
0
  auto modifier = url_pattern_part_modifier::none;
11318
  // If modifier token is not null:
11319
0
  if (modifier_token) {
11320
    // If modifier token's value is "?" then set modifier to "optional".
11321
0
    if (modifier_token->value == "?") {
11322
0
      modifier = url_pattern_part_modifier::optional;
11323
0
    } else if (modifier_token->value == "*") {
11324
      // Otherwise if modifier token's value is "*" then set modifier to
11325
      // "zero-or-more".
11326
0
      modifier = url_pattern_part_modifier::zero_or_more;
11327
0
    } else if (modifier_token->value == "+") {
11328
      // Otherwise if modifier token's value is "+" then set modifier to
11329
      // "one-or-more".
11330
0
      modifier = url_pattern_part_modifier::one_or_more;
11331
0
    }
11332
0
  }
11333
  // If name token is null and regexp or wildcard token is null and modifier
11334
  // is "none":
11335
0
  if (!name_token && !regexp_or_wildcard_token &&
11336
0
      modifier == url_pattern_part_modifier::none) {
11337
    // Append prefix to the end of parser's pending fixed value.
11338
0
    pending_fixed_value.append(prefix);
11339
0
    return std::nullopt;
11340
0
  }
11341
  // Run maybe add a part from the pending fixed value given parser.
11342
0
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
11343
0
    return *error;
11344
0
  }
11345
  // If name token is null and regexp or wildcard token is null:
11346
0
  if (!name_token && !regexp_or_wildcard_token) {
11347
    // Assert: suffix is the empty string.
11348
0
    ADA_ASSERT_TRUE(suffix.empty());
11349
    // If prefix is the empty string, then return.
11350
0
    if (prefix.empty()) return std::nullopt;
11351
    // Let encoded value be the result of running parser's encoding callback
11352
    // given prefix.
11353
0
    auto encoded_value = encoding_callback(prefix);
11354
0
    if (!encoded_value) {
11355
0
      return encoded_value.error();
11356
0
    }
11357
    // Let part be a new part whose type is "fixed-text", value is encoded
11358
    // value, and modifier is modifier.
11359
    // Append part to parser's part list.
11360
0
    parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11361
0
                       std::move(*encoded_value), modifier);
11362
0
    return std::nullopt;
11363
0
  }
11364
  // Let regexp value be the empty string.
11365
0
  std::string regexp_value{};
11366
  // If regexp or wildcard token is null, then set regexp value to parser's
11367
  // segment wildcard regexp.
11368
0
  if (!regexp_or_wildcard_token) {
11369
0
    regexp_value = segment_wildcard_regexp;
11370
0
  } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) {
11371
    // Otherwise if regexp or wildcard token's type is "asterisk", then set
11372
    // regexp value to the full wildcard regexp value.
11373
0
    regexp_value = ".*";
11374
0
  } else {
11375
    // Otherwise set regexp value to regexp or wildcard token's value.
11376
0
    regexp_value = regexp_or_wildcard_token->value;
11377
0
  }
11378
  // Let type be "regexp".
11379
0
  auto type = url_pattern_part_type::REGEXP;
11380
  // If regexp value is parser's segment wildcard regexp:
11381
0
  if (regexp_value == segment_wildcard_regexp) {
11382
    // Set type to "segment-wildcard".
11383
0
    type = url_pattern_part_type::SEGMENT_WILDCARD;
11384
    // Set regexp value to the empty string.
11385
0
    regexp_value.clear();
11386
0
  } else if (regexp_value == ".*") {
11387
    // Otherwise if regexp value is the full wildcard regexp value:
11388
    // Set type to "full-wildcard".
11389
0
    type = url_pattern_part_type::FULL_WILDCARD;
11390
    // Set regexp value to the empty string.
11391
0
    regexp_value.clear();
11392
0
  }
11393
  // Let name be the empty string.
11394
0
  std::string name{};
11395
  // If name token is not null, then set name to name token's value.
11396
0
  if (name_token) {
11397
0
    name = name_token->value;
11398
0
  } else if (regexp_or_wildcard_token != nullptr) {
11399
    // Otherwise if regexp or wildcard token is not null:
11400
    // Set name to parser's next numeric name, serialized.
11401
0
    name = std::to_string(next_numeric_name);
11402
    // Increment parser's next numeric name by 1.
11403
0
    next_numeric_name++;
11404
0
  }
11405
  // If the result of running is a duplicate name given parser and name is
11406
  // true, then throw a TypeError. Tracked in a set so this stays O(1): a
11407
  // linear scan of the part list here is O(n^2) over a pattern string with n
11408
  // named parts (e.g. "/*" repeated), an algorithmic-complexity DoS.
11409
0
  if (!name.empty() && !seen_names.insert(name).second) {
11410
0
    return errors::type_error;
11411
0
  }
11412
  // Let encoded prefix be the result of running parser's encoding callback
11413
  // given prefix.
11414
0
  auto encoded_prefix = encoding_callback(prefix);
11415
0
  if (!encoded_prefix) return encoded_prefix.error();
11416
  // Let encoded suffix be the result of running parser's encoding callback
11417
  // given suffix.
11418
0
  auto encoded_suffix = encoding_callback(suffix);
11419
0
  if (!encoded_suffix) return encoded_suffix.error();
11420
  // Let part be a new part whose type is type, value is regexp value,
11421
  // modifier is modifier, name is name, prefix is encoded prefix, and suffix
11422
  // is encoded suffix.
11423
  // Append part to parser's part list.
11424
0
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
11425
0
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
11426
0
  return std::nullopt;
11427
0
}
11428
11429
template <url_pattern_encoding_callback F>
11430
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
11431
    std::string_view input, url_pattern_compile_component_options& options,
11432
0
    F& encoding_callback) {
11433
0
  ada_log("parse_pattern_string input=", input);
11434
  // Let parser be a new pattern parser whose encoding callback is encoding
11435
  // callback and segment wildcard regexp is the result of running generate a
11436
  // segment wildcard regexp given options.
11437
0
  auto parser = url_pattern_parser<F>(
11438
0
      encoding_callback, generate_segment_wildcard_regexp(options));
11439
  // Set parser's token list to the result of running tokenize given input and
11440
  // "strict".
11441
0
  auto tokenize_result = tokenize(input, token_policy::strict);
11442
0
  if (!tokenize_result) {
11443
0
    ada_log("parse_pattern_string tokenize failed");
11444
0
    return tl::unexpected(tokenize_result.error());
11445
0
  }
11446
0
  parser.tokens = std::move(*tokenize_result);
11447
11448
  // While parser's index is less than parser's token list's size:
11449
0
  while (parser.can_continue()) {
11450
    // Let char token be the result of running try to consume a token given
11451
    // parser and "char".
11452
0
    auto char_token = parser.try_consume_token(token_type::CHAR);
11453
    // Let name token be the result of running try to consume a token given
11454
    // parser and "name".
11455
0
    auto name_token = parser.try_consume_token(token_type::NAME);
11456
    // Let regexp or wildcard token be the result of running try to consume a
11457
    // regexp or wildcard token given parser and name token.
11458
0
    auto regexp_or_wildcard_token =
11459
0
        parser.try_consume_regexp_or_wildcard_token(name_token);
11460
    // If name token is not null or regexp or wildcard token is not null:
11461
0
    if (name_token || regexp_or_wildcard_token) {
11462
      // Let prefix be the empty string.
11463
0
      std::string prefix{};
11464
      // If char token is not null then set prefix to char token's value.
11465
0
      if (char_token) prefix = char_token->value;
11466
      // If prefix is not the empty string and not options's prefix code point:
11467
0
      if (!prefix.empty() && prefix != options.get_prefix()) {
11468
        // Append prefix to the end of parser's pending fixed value.
11469
0
        parser.pending_fixed_value.append(prefix);
11470
        // Set prefix to the empty string.
11471
0
        prefix.clear();
11472
0
      }
11473
      // Run maybe add a part from the pending fixed value given parser.
11474
0
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11475
0
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
11476
0
        return tl::unexpected(*error);
11477
0
      }
11478
      // Let modifier token be the result of running try to consume a modifier
11479
      // token given parser.
11480
0
      auto modifier_token = parser.try_consume_modifier_token();
11481
      // Run add a part given parser, prefix, name token, regexp or wildcard
11482
      // token, the empty string, and modifier token.
11483
0
      if (auto error =
11484
0
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
11485
0
                              modifier_token)) {
11486
0
        ada_log("parser.add_part failed");
11487
0
        return tl::unexpected(*error);
11488
0
      }
11489
      // Continue.
11490
0
      continue;
11491
0
    }
11492
11493
    // Let fixed token be char token.
11494
0
    auto fixed_token = char_token;
11495
    // If fixed token is null, then set fixed token to the result of running try
11496
    // to consume a token given parser and "escaped-char".
11497
0
    if (!fixed_token)
11498
0
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
11499
    // If fixed token is not null:
11500
0
    if (fixed_token) {
11501
      // Append fixed token's value to parser's pending fixed value.
11502
0
      parser.pending_fixed_value.append(fixed_token->value);
11503
      // Continue.
11504
0
      continue;
11505
0
    }
11506
    // Let open token be the result of running try to consume a token given
11507
    // parser and "open".
11508
0
    auto open_token = parser.try_consume_token(token_type::OPEN);
11509
    // If open token is not null:
11510
0
    if (open_token) {
11511
      // Set prefix be the result of running consume text given parser.
11512
0
      auto prefix_ = parser.consume_text();
11513
      // Set name token to the result of running try to consume a token given
11514
      // parser and "name".
11515
0
      name_token = parser.try_consume_token(token_type::NAME);
11516
      // Set regexp or wildcard token to the result of running try to consume a
11517
      // regexp or wildcard token given parser and name token.
11518
0
      regexp_or_wildcard_token =
11519
0
          parser.try_consume_regexp_or_wildcard_token(name_token);
11520
      // Let suffix be the result of running consume text given parser.
11521
0
      auto suffix_ = parser.consume_text();
11522
      // Run consume a required token given parser and "close".
11523
0
      if (!parser.consume_required_token(token_type::CLOSE)) {
11524
0
        ada_log("parser.consume_required_token failed");
11525
0
        return tl::unexpected(errors::type_error);
11526
0
      }
11527
      // Set modifier token to the result of running try to consume a modifier
11528
      // token given parser.
11529
0
      auto modifier_token = parser.try_consume_modifier_token();
11530
      // Run add a part given parser, prefix, name token, regexp or wildcard
11531
      // token, suffix, and modifier token.
11532
0
      if (auto error =
11533
0
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
11534
0
                              suffix_, modifier_token)) {
11535
0
        return tl::unexpected(*error);
11536
0
      }
11537
      // Continue.
11538
0
      continue;
11539
0
    }
11540
    // Run maybe add a part from the pending fixed value given parser.
11541
0
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11542
0
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
11543
0
      return tl::unexpected(*error);
11544
0
    }
11545
    // Run consume a required token given parser and "end".
11546
0
    if (!parser.consume_required_token(token_type::END)) {
11547
0
      return tl::unexpected(errors::type_error);
11548
0
    }
11549
0
  }
11550
0
  ada_log("parser.parts size is: ", parser.parts.size());
11551
  // Return parser's part list.
11552
0
  return parser.parts;
11553
0
}
11554
11555
template <url_pattern_regex::regex_concept regex_provider>
11556
bool protocol_component_matches_special_scheme(
11557
    url_pattern_component<regex_provider>& component) {
11558
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
11559
  switch (component.type) {
11560
    case url_pattern_component_type::EMPTY:
11561
      // Empty pattern can't match any special scheme
11562
      return false;
11563
    case url_pattern_component_type::EXACT_MATCH:
11564
      // Direct string comparison for exact match patterns
11565
      return component.exact_match_value == "http" ||
11566
             component.exact_match_value == "https" ||
11567
             component.exact_match_value == "ws" ||
11568
             component.exact_match_value == "wss" ||
11569
             component.exact_match_value == "ftp" ||
11570
             component.exact_match_value == "file";
11571
    case url_pattern_component_type::FULL_WILDCARD:
11572
      // Full wildcard matches everything including special schemes
11573
      return true;
11574
    case url_pattern_component_type::REGEXP:
11575
      // Fall back to regex matching for complex patterns
11576
      auto& regex = component.regexp;
11577
      return regex_provider::regex_match("http", regex) ||
11578
             regex_provider::regex_match("https", regex) ||
11579
             regex_provider::regex_match("ws", regex) ||
11580
             regex_provider::regex_match("wss", regex) ||
11581
             regex_provider::regex_match("ftp", regex) ||
11582
             regex_provider::regex_match("file", regex);
11583
  }
11584
  ada::unreachable();
11585
}
11586
11587
template <url_pattern_regex::regex_concept regex_provider>
11588
inline std::optional<errors> constructor_string_parser<
11589
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
11590
  ada_log(
11591
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
11592
      "flag");
11593
  // Let protocol string be the result of running make a component string given
11594
  // parser.
11595
  auto protocol_string = make_component_string();
11596
  // Let protocol component be the result of compiling a component given
11597
  // protocol string, canonicalize a protocol, and default options.
11598
  auto protocol_component = url_pattern_component<regex_provider>::compile(
11599
      protocol_string, canonicalize_protocol,
11600
      url_pattern_compile_component_options::DEFAULT);
11601
  if (!protocol_component) {
11602
    ada_log("url_pattern_component::compile failed for protocol_string ",
11603
            protocol_string);
11604
    return protocol_component.error();
11605
  }
11606
  // If the result of running protocol component matches a special scheme given
11607
  // protocol component is true, then set parser's protocol matches a special
11608
  // scheme flag to true.
11609
  if (protocol_component_matches_special_scheme(*protocol_component)) {
11610
    protocol_matches_a_special_scheme_flag = true;
11611
  }
11612
  return std::nullopt;
11613
}
11614
11615
template <url_pattern_regex::regex_concept regex_provider>
11616
tl::expected<url_pattern_init, errors>
11617
constructor_string_parser<regex_provider>::parse(std::string_view input) {
11618
  ada_log("constructor_string_parser::parse input=", input);
11619
  // Let parser be a new constructor string parser whose input is input and
11620
  // token list is the result of running tokenize given input and "lenient".
11621
  auto token_list = tokenize(input, token_policy::lenient);
11622
  if (!token_list) {
11623
    return tl::unexpected(token_list.error());
11624
  }
11625
  auto parser = constructor_string_parser(input, std::move(*token_list));
11626
11627
  // While parser's token index is less than parser's token list size:
11628
  while (parser.token_index < parser.token_list.size()) {
11629
    // Set parser's token increment to 1.
11630
    parser.token_increment = 1;
11631
11632
    // If parser's token list[parser's token index]'s type is "end" then:
11633
    if (parser.token_list[parser.token_index].type == token_type::END) {
11634
      // If parser's state is "init":
11635
      if (parser.state == State::INIT) {
11636
        // Run rewind given parser.
11637
        parser.rewind();
11638
        // If the result of running is a hash prefix given parser is true, then
11639
        // run change state given parser, "hash" and 1.
11640
        if (parser.is_hash_prefix()) {
11641
          parser.change_state(State::HASH, 1);
11642
        } else if (parser.is_search_prefix()) {
11643
          // Otherwise if the result of running is a search prefix given parser
11644
          // is true: Run change state given parser, "search" and 1.
11645
          parser.change_state(State::SEARCH, 1);
11646
        } else {
11647
          // Run change state given parser, "pathname" and 0.
11648
          parser.change_state(State::PATHNAME, 0);
11649
        }
11650
        // Increment parser's token index by parser's token increment.
11651
        parser.token_index += parser.token_increment;
11652
        // Continue.
11653
        continue;
11654
      }
11655
11656
      if (parser.state == State::AUTHORITY) {
11657
        // If parser's state is "authority":
11658
        // Run rewind and set state given parser, and "hostname".
11659
        parser.rewind();
11660
        parser.change_state(State::HOSTNAME, 0);
11661
        // Increment parser's token index by parser's token increment.
11662
        parser.token_index += parser.token_increment;
11663
        // Continue.
11664
        continue;
11665
      }
11666
11667
      // Run change state given parser, "done" and 0.
11668
      parser.change_state(State::DONE, 0);
11669
      // Break.
11670
      break;
11671
    }
11672
11673
    // If the result of running is a group open given parser is true:
11674
    if (parser.is_group_open()) {
11675
      // Increment parser's group depth by 1.
11676
      parser.group_depth += 1;
11677
      // Increment parser's token index by parser's token increment.
11678
      parser.token_index += parser.token_increment;
11679
      // Continue.
11680
      continue;
11681
    }
11682
11683
    // If parser's group depth is greater than 0:
11684
    if (parser.group_depth > 0) {
11685
      // If the result of running is a group close given parser is true, then
11686
      // decrement parser's group depth by 1.
11687
      if (parser.is_group_close()) {
11688
        parser.group_depth -= 1;
11689
      } else {
11690
        // Increment parser's token index by parser's token increment.
11691
        parser.token_index += parser.token_increment;
11692
        continue;
11693
      }
11694
    }
11695
11696
    // Switch on parser's state and run the associated steps:
11697
    switch (parser.state) {
11698
      case State::INIT: {
11699
        // If the result of running is a protocol suffix given parser is true:
11700
        if (parser.is_protocol_suffix()) {
11701
          // Run rewind and set state given parser and "protocol".
11702
          parser.rewind();
11703
          parser.change_state(State::PROTOCOL, 0);
11704
        }
11705
        break;
11706
      }
11707
      case State::PROTOCOL: {
11708
        // If the result of running is a protocol suffix given parser is true:
11709
        if (parser.is_protocol_suffix()) {
11710
          // Run compute protocol matches a special scheme flag given parser.
11711
          if (const auto error =
11712
                  parser.compute_protocol_matches_special_scheme_flag()) {
11713
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11714
            return tl::unexpected(*error);
11715
          }
11716
          // Let next state be "pathname".
11717
          auto next_state = State::PATHNAME;
11718
          // Let skip be 1.
11719
          auto skip = 1;
11720
          // If the result of running next is authority slashes given parser is
11721
          // true:
11722
          if (parser.next_is_authority_slashes()) {
11723
            // Set next state to "authority".
11724
            next_state = State::AUTHORITY;
11725
            // Set skip to 3.
11726
            skip = 3;
11727
          } else if (parser.protocol_matches_a_special_scheme_flag) {
11728
            // Otherwise if parser's protocol matches a special scheme flag is
11729
            // true, then set next state to "authority".
11730
            next_state = State::AUTHORITY;
11731
          }
11732
11733
          // Run change state given parser, next state, and skip.
11734
          parser.change_state(next_state, skip);
11735
        }
11736
        break;
11737
      }
11738
      case State::AUTHORITY: {
11739
        // If the result of running is an identity terminator given parser is
11740
        // true, then run rewind and set state given parser and "username".
11741
        if (parser.is_an_identity_terminator()) {
11742
          parser.rewind();
11743
          parser.change_state(State::USERNAME, 0);
11744
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11745
                   parser.is_hash_prefix()) {
11746
          // Otherwise if any of the following are true:
11747
          // - the result of running is a pathname start given parser;
11748
          // - the result of running is a search prefix given parser; or
11749
          // - the result of running is a hash prefix given parser,
11750
          // then run rewind and set state given parser and "hostname".
11751
          parser.rewind();
11752
          parser.change_state(State::HOSTNAME, 0);
11753
        }
11754
        break;
11755
      }
11756
      case State::USERNAME: {
11757
        // If the result of running is a password prefix given parser is true,
11758
        // then run change state given parser, "password", and 1.
11759
        if (parser.is_password_prefix()) {
11760
          parser.change_state(State::PASSWORD, 1);
11761
        } else if (parser.is_an_identity_terminator()) {
11762
          // Otherwise if the result of running is an identity terminator given
11763
          // parser is true, then run change state given parser, "hostname",
11764
          // and 1.
11765
          parser.change_state(State::HOSTNAME, 1);
11766
        }
11767
        break;
11768
      }
11769
      case State::PASSWORD: {
11770
        // If the result of running is an identity terminator given parser is
11771
        // true, then run change state given parser, "hostname", and 1.
11772
        if (parser.is_an_identity_terminator()) {
11773
          parser.change_state(State::HOSTNAME, 1);
11774
        }
11775
        break;
11776
      }
11777
      case State::HOSTNAME: {
11778
        // If the result of running is an IPv6 open given parser is true, then
11779
        // increment parser's hostname IPv6 bracket depth by 1.
11780
        if (parser.is_an_ipv6_open()) {
11781
          parser.hostname_ipv6_bracket_depth += 1;
11782
        } else if (parser.is_an_ipv6_close()) {
11783
          // Otherwise if the result of running is an IPv6 close given parser is
11784
          // true, then decrement parser's hostname IPv6 bracket depth by 1.
11785
          parser.hostname_ipv6_bracket_depth -= 1;
11786
        } else if (parser.is_port_prefix() &&
11787
                   parser.hostname_ipv6_bracket_depth == 0) {
11788
          // Otherwise if the result of running is a port prefix given parser is
11789
          // true and parser's hostname IPv6 bracket depth is zero, then run
11790
          // change state given parser, "port", and 1.
11791
          parser.change_state(State::PORT, 1);
11792
        } else if (parser.is_pathname_start()) {
11793
          // Otherwise if the result of running is a pathname start given parser
11794
          // is true, then run change state given parser, "pathname", and 0.
11795
          parser.change_state(State::PATHNAME, 0);
11796
        } else if (parser.is_search_prefix()) {
11797
          // Otherwise if the result of running is a search prefix given parser
11798
          // is true, then run change state given parser, "search", and 1.
11799
          parser.change_state(State::SEARCH, 1);
11800
        } else if (parser.is_hash_prefix()) {
11801
          // Otherwise if the result of running is a hash prefix given parser is
11802
          // true, then run change state given parser, "hash", and 1.
11803
          parser.change_state(State::HASH, 1);
11804
        }
11805
11806
        break;
11807
      }
11808
      case State::PORT: {
11809
        // If the result of running is a pathname start given parser is true,
11810
        // then run change state given parser, "pathname", and 0.
11811
        if (parser.is_pathname_start()) {
11812
          parser.change_state(State::PATHNAME, 0);
11813
        } else if (parser.is_search_prefix()) {
11814
          // Otherwise if the result of running is a search prefix given parser
11815
          // is true, then run change state given parser, "search", and 1.
11816
          parser.change_state(State::SEARCH, 1);
11817
        } else if (parser.is_hash_prefix()) {
11818
          // Otherwise if the result of running is a hash prefix given parser is
11819
          // true, then run change state given parser, "hash", and 1.
11820
          parser.change_state(State::HASH, 1);
11821
        }
11822
        break;
11823
      }
11824
      case State::PATHNAME: {
11825
        // If the result of running is a search prefix given parser is true,
11826
        // then run change state given parser, "search", and 1.
11827
        if (parser.is_search_prefix()) {
11828
          parser.change_state(State::SEARCH, 1);
11829
        } else if (parser.is_hash_prefix()) {
11830
          // Otherwise if the result of running is a hash prefix given parser is
11831
          // true, then run change state given parser, "hash", and 1.
11832
          parser.change_state(State::HASH, 1);
11833
        }
11834
        break;
11835
      }
11836
      case State::SEARCH: {
11837
        // If the result of running is a hash prefix given parser is true, then
11838
        // run change state given parser, "hash", and 1.
11839
        if (parser.is_hash_prefix()) {
11840
          parser.change_state(State::HASH, 1);
11841
        }
11842
        break;
11843
      }
11844
      case State::HASH: {
11845
        // Do nothing
11846
        break;
11847
      }
11848
      default: {
11849
        // Assert: This step is never reached.
11850
        unreachable();
11851
      }
11852
    }
11853
11854
    // Increment parser's token index by parser's token increment.
11855
    parser.token_index += parser.token_increment;
11856
  }
11857
11858
  // If parser's result contains "hostname" and not "port", then set parser's
11859
  // result["port"] to the empty string.
11860
  if (parser.result.hostname && !parser.result.port) {
11861
    parser.result.port = "";
11862
  }
11863
11864
  // Return parser's result.
11865
  return parser.result;
11866
}
11867
11868
}  // namespace ada::url_pattern_helpers
11869
#endif  // ADA_INCLUDE_URL_PATTERN
11870
#endif
11871
/* end file include/ada/url_pattern_helpers-inl.h */
11872
/* begin file include/ada/url_pattern_list.h */
11873
/**
11874
 * @file url_pattern_list.h
11875
 * @brief Compiled set-level matching over URLPattern pathname patterns.
11876
 *
11877
 * `ada::url_pattern_list` answers the question "which of my N routes matches
11878
 * this pathname, and what are the parameter values?" in a single walk
11879
 * instead of a loop of N `url_pattern::exec` calls. The whole route set is
11880
 * compiled once, by ada::parse_url_pattern_list, into a segment trie with
11881
 * per-node dispatch (direct compares for small fanouts, a first-byte index
11882
 * at the root, perfect-hash projection for wide nodes). Matching a pathname
11883
 * against the compiled set is allocation-free and does not execute any
11884
 * regular expression for routes written in the common static / ":param" /
11885
 * "*" subset; other routes are matched through the regex provider, exactly
11886
 * as ada::url_pattern does.
11887
 *
11888
 * This implements the route-set ("URLPatternList") use case discussed in
11889
 * https://github.com/whatwg/urlpattern/issues/166 for the pathname component.
11890
 *
11891
 * Correctness does not depend on the fast path: inputs or routes that exceed
11892
 * the fast-path limits (documented on the constants in
11893
 * `ada::url_pattern_list_limits`) are matched by a sequential fallback with
11894
 * identical priority semantics, never rejected and never answered wrongly.
11895
 *
11896
 * @see https://urlpattern.spec.whatwg.org/
11897
 */
11898
#ifndef ADA_URL_PATTERN_LIST_H
11899
#define ADA_URL_PATTERN_LIST_H
11900
11901
11902
#include <array>
11903
#include <cstddef>
11904
#include <cstdint>
11905
#include <optional>
11906
#include <span>
11907
#include <string>
11908
#include <string_view>
11909
#include <vector>
11910
11911
#if ADA_INCLUDE_URL_PATTERN
11912
namespace ada {
11913
11914
/**
11915
 * The limits of the url_pattern_list fast path. None of them changes what a
11916
 * list matches: routes and inputs beyond a limit are matched by a sequential
11917
 * fallback with identical semantics.
11918
 * @namespace ada::url_pattern_list_limits
11919
 */
11920
namespace url_pattern_list_limits {
11921
11922
/**
11923
 * Maximum number of captures (":param" groups plus one trailing "*" group)
11924
 * a route may declare and still be compiled into the fast path. Routes with
11925
 * more groups are matched by the sequential fallback; their match is still
11926
 * correct, but only the first `max_captures_per_route` captures are reported
11927
 * (see url_pattern_list_match_result::captures_truncated).
11928
 */
11929
inline constexpr uint32_t max_captures_per_route = 8;
11930
11931
/**
11932
 * Maximum number of '/'-separated segments an input pathname may have and
11933
 * still be matched by the fast path. Longer inputs fall back to the
11934
 * sequential matcher and are still matched correctly.
11935
 */
11936
inline constexpr uint32_t max_fast_path_segments = 24;
11937
11938
/**
11939
 * Maximum input pathname length (bytes) accepted by the fast path; segment
11940
 * offsets are tracked in 16-bit integers internally. Longer inputs fall back
11941
 * to the sequential matcher and are still matched correctly.
11942
 */
11943
inline constexpr uint32_t max_fast_path_pathname_length = 4096;
11944
11945
/**
11946
 * Maximum number of pattern segments a route may have and still be compiled
11947
 * into the trie fast path. Deeper routes are matched by the sequential
11948
 * fallback with identical semantics.
11949
 */
11950
inline constexpr uint32_t max_trie_pattern_segments = 16;
11951
11952
}  // namespace url_pattern_list_limits
11953
11954
/**
11955
 * The result of url_pattern_list::match. A winning route reports its group
11956
 * values in one of two forms, mirroring url_pattern_list::group_names():
11957
 *
11958
 * - Routes in the static / ":param" / "*" subset report zero-allocation
11959
 *   captures: (offset, length) slices of the pathname passed to match, in
11960
 *   capture order (":param" groups left to right, then the "*" group).
11961
 *   `regexp_route` is false and `regexp_groups` is empty.
11962
 * - Routes matched through the regex provider (patterns with custom regexp
11963
 *   groups or "?" / "+" / "*" modifiers) report the group values the
11964
 *   provider's regex_search returned, exactly as url_pattern::exec would for
11965
 *   the pathname component: `regexp_route` is true, `regexp_groups[i]` is the
11966
 *   value of group_names(route_index)[i] (nullopt for a group that did not
11967
 *   participate), and capture_count is 0.
11968
 *
11969
 * Routes with more than url_pattern_list_limits::max_captures_per_route
11970
 * groups report only the first max_captures_per_route slices and set
11971
 * captures_truncated.
11972
 */
11973
struct url_pattern_list_match_result {
11974
  /** One capture: an (offset, length) slice of the matched pathname. */
11975
  struct capture {
11976
    uint32_t offset = 0;
11977
    uint32_t length = 0;
11978
  };
11979
  /** Index of the winning route in the creation order, or -1 for no match. */
11980
  int32_t route_index = -1;
11981
  /** Number of valid entries in captures. */
11982
  uint32_t capture_count = 0;
11983
  /** True when the route has more groups than could be reported as slices. */
11984
  bool captures_truncated = false;
11985
  /** True when the winning route was matched through the regex provider; its
11986
   * group values are in regexp_groups. */
11987
  bool regexp_route = false;
11988
  std::array<capture, url_pattern_list_limits::max_captures_per_route>
11989
      captures{};
11990
  /** Group values of a regexp route, as returned by the provider. */
11991
  std::vector<std::optional<std::string>> regexp_groups{};
11992
11993
0
  [[nodiscard]] bool has_match() const noexcept { return route_index >= 0; }
11994
};
11995
11996
/**
11997
 * @private
11998
 * The compiled tables url_pattern_list::match walks, and the inline matcher
11999
 * over them (url_pattern_list-inl.h). Nothing here is part of the supported
12000
 * API. The route-set compiler that produces these tables is not part of the
12001
 * public headers at all (src/url_pattern_list_compiler.h).
12002
 * @namespace ada::url_pattern_list_detail
12003
 */
12004
namespace url_pattern_list_detail {
12005
12006
/** @private One trie node. Nodes with a projection dispatch keep their hash
12007
 * payload in a separate hash_record, so the common 0/1/direct nodes stay at
12008
 * 24 bytes. */
12009
struct node_record {
12010
  // First static edge (edges are laid out per node, contiguously).
12011
  int32_t first_child = 0;
12012
  // Child node index, -2 - route (pure-leaf shortcut), or -1.
12013
  int32_t param_child = -1;
12014
  // Route index of a "*" ending at this prefix, or -1.
12015
  int32_t wild_route = -1;
12016
  // Route ending exactly here, or -1.
12017
  int32_t terminal_route = -1;
12018
  uint16_t n_static = 0;
12019
  // 0 none, 1 direct compares, 2 projection, 3 linear scan, 4 first-byte
12020
  // index (root only).
12021
  uint8_t dispatch = 0;
12022
  // 1 if the node still has a param/wildcard alternative to backtrack to.
12023
  uint8_t has_alternative = 0;
12024
  // Index of the node's hash_record (dispatch == 2 only).
12025
  uint32_t hash_index = 0;
12026
};
12027
12028
/** @private Projection-dispatch payload of a hashed node. */
12029
struct hash_record {
12030
  uint64_t multiplier = 0;
12031
  uint32_t slot_base = 0;
12032
  // Witness plan: 3 nibble ids | count << 12 | use_len << 14.
12033
  uint16_t witness_plan = 0;
12034
  // Slot table size == 1 << slot_bits.
12035
  uint8_t slot_bits = 0;
12036
  uint8_t reserved = 0;
12037
};
12038
12039
/** @private One static edge; keys <= 16 bytes verify from the packed
12040
 * windows alone. */
12041
struct edge_record {
12042
  // Key bytes [0, min(8, len)), little-endian, zero-padded.
12043
  uint64_t prefix = 0;
12044
  // Key bytes [max(0, len - 8), len), little-endian, zero-padded.
12045
  uint64_t suffix = 0;
12046
  // Child node index; negative values encode -2 - route pure-leaf shortcuts.
12047
  int32_t node = -1;
12048
  // Full key in the blob (read only when key_length > 16).
12049
  uint32_t key_offset = 0;
12050
  uint16_t key_length = 0;
12051
};
12052
12053
/** @private How one route of the set is matched. */
12054
enum class route_mode : uint8_t {
12055
  // Compiled into the trie fast path.
12056
  trie = 0,
12057
  // Safe static/":param"/"*" syntax beyond a fast-path build limit: matched
12058
  // by the sequential segment matcher.
12059
  sequential = 1,
12060
  // Needs URLPattern regexp semantics: matched through the regex provider.
12061
  regexp = 2,
12062
};
12063
12064
/** @private Everything the matcher knows about one route after a hit. */
12065
struct route_record {
12066
  // Packed per-segment kinds, two bits per segment, most significant first;
12067
  // (kind_sequence, kind_length, route index) compared as a tuple is the
12068
  // specificity order.
12069
  uint64_t kind_sequence = 0;
12070
  // Sequential-mode routes: their segments in the segment table.
12071
  uint32_t segment_first = 0;
12072
  uint32_t segment_count = 0;
12073
  // Auxiliary routes that can outrank this route when it wins the fast
12074
  // path: a range of the aux table (usually empty).
12075
  uint32_t challenger_first = 0;
12076
  uint32_t challenger_count = 0;
12077
  // Ordinal among the regexp-mode routes, or -1.
12078
  int32_t regexp_component = -1;
12079
  uint8_t kind_length = 0;
12080
  route_mode mode = route_mode::sequential;
12081
  // Trie-mode routes: capture positions.
12082
  uint8_t n_params = 0;
12083
  uint8_t wild = 0;
12084
  std::array<uint8_t, url_pattern_list_limits::max_captures_per_route>
12085
      param_positions{};
12086
};
12087
12088
/** @private One pattern segment of a sequential-mode route. */
12089
struct segment_record {
12090
  uint32_t text_offset = 0;
12091
  uint32_t text_length = 0;
12092
  // 0 literal, 1 ":param", 2 "*".
12093
  uint8_t kind = 0;
12094
};
12095
12096
/**
12097
 * @private
12098
 * The compiled route set: one arena holding every table, addressed by
12099
 * section offsets, plus the per-route group names. Sections are 8-byte
12100
 * aligned; the blob is zero-padded by 8 bytes so short key windows can be
12101
 * loaded whole.
12102
 */
12103
struct compiled_routes {
12104
  std::vector<uint8_t> arena{};
12105
  std::vector<std::vector<std::string>> group_names{};
12106
  uint32_t nodes_offset = 0;
12107
  uint32_t hashes_offset = 0;
12108
  uint32_t edges_offset = 0;
12109
  uint32_t slots_offset = 0;
12110
  uint32_t blob_offset = 0;
12111
  uint32_t routes_offset = 0;
12112
  uint32_t segments_offset = 0;
12113
  uint32_t aux_offset = 0;
12114
  uint32_t root_index_offset = 0;
12115
  uint32_t n_routes = 0;
12116
  // All auxiliary routes (regexp mode and sequential mode) in insertion
12117
  // order: the aux table's first n_aux_all entries; challenger ranges
12118
  // follow.
12119
  uint32_t n_aux_all = 0;
12120
  uint8_t ignore_case = 0;
12121
12122
  template <typename T>
12123
0
  [[nodiscard]] const T* section(uint32_t offset) const noexcept {
12124
0
    return reinterpret_cast<const T*>(arena.data() + offset);
12125
0
  }
Unexecuted instantiation: char const* ada::url_pattern_list_detail::compiled_routes::section<char>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::route_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::route_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::segment_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::segment_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::node_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::node_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::hash_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::hash_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::edge_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::edge_record>(unsigned int) const
Unexecuted instantiation: unsigned char const* ada::url_pattern_list_detail::compiled_routes::section<unsigned char>(unsigned int) const
Unexecuted instantiation: unsigned short const* ada::url_pattern_list_detail::compiled_routes::section<unsigned short>(unsigned int) const
12126
};
12127
12128
/** @private Result of the fast-path engine or of the sequential matcher. */
12129
struct engine_result {
12130
  using capture = url_pattern_list_match_result::capture;
12131
  int32_t route = -1;
12132
  uint32_t capture_count = 0;
12133
  // False when the input exceeded a fast-path limit and the caller must run
12134
  // the sequential fallback over the whole route set.
12135
  bool within_fast_path = true;
12136
  bool captures_truncated = false;
12137
  std::array<capture, url_pattern_list_limits::max_captures_per_route>
12138
      captures{};
12139
};
12140
12141
/**
12142
 * @private
12143
 * Parses and compiles a set of pathname patterns (already base-URL
12144
 * processed). Routes that need the regex provider come back with
12145
 * route_record::regexp_component set to their ordinal in insertion order;
12146
 * the caller compiles those components and fills their group names.
12147
 */
12148
tl::expected<compiled_routes, errors> compile_pathname_patterns(
12149
    std::span<const std::string> patterns, bool ignore_case);
12150
12151
/**
12152
 * @private
12153
 * Sequential reference matcher for one non-regexp route of the compiled set;
12154
 * matches any input (no fast-path limits) with the same semantics as the
12155
 * compiled engine. With `fold_input`, input bytes are ASCII case-folded on
12156
 * the fly (the compiled literals are already folded). On a match, fills
12157
 * `result` (route index is NOT set) and returns true.
12158
 */
12159
bool match_route_sequential(const compiled_routes& tables, uint32_t route,
12160
                            std::string_view pathname, bool fold_input,
12161
                            engine_result& result) noexcept;
12162
12163
/**
12164
 * @private
12165
 * True when the bytes [p, p + n) hold no line terminator. A "*" segment
12166
 * compiles to "(.*)" in the URLPattern regexp, and "." in an ECMAScript
12167
 * regular expression does not match LF or CR, so a wildcard may not capture
12168
 * past one. Deliberately not inlined into the walk (it costs the static and
12169
 * ":param" paths, which never run it), and deliberately defined in
12170
 * url_pattern_list.cpp rather than here: MSVC's ada_never_inline carries no
12171
 * inline linkage, so a definition in this header is emitted in every
12172
 * translation unit.
12173
 */
12174
bool wildcard_tail_ok(const char* p, uint32_t n) noexcept;
12175
12176
/**
12177
 * @private
12178
 * Cheap pre-check for a regexp-mode route before running the provider: the
12179
 * part of the route's segment shape that is certain (see the compiler's
12180
 * approximate_kind_sequence) must fit the pathname -- its anchored literal
12181
 * segments, and its exact segment count when every group is a ":name"
12182
 * segment wildcard. False proves the regular expression cannot match.
12183
 */
12184
bool match_regexp_shape(const compiled_routes& tables, uint32_t route,
12185
                        std::string_view pathname, bool fold_input) noexcept;
12186
12187
/**
12188
 * @private
12189
 * True when route `a` (at insertion index `a_index`) outranks route `b` (at
12190
 * `b_index`): lexicographically smaller kind sequence, insertion order as the
12191
 * tiebreak.
12192
 */
12193
constexpr bool outranks(uint64_t a_sequence, uint32_t a_length, size_t a_index,
12194
                        uint64_t b_sequence, uint32_t b_length,
12195
0
                        size_t b_index) noexcept {
12196
0
  if (a_sequence != b_sequence) {
12197
0
    return a_sequence < b_sequence;
12198
0
  }
12199
0
  if (a_length != b_length) {
12200
0
    return a_length < b_length;
12201
0
  }
12202
0
  return a_index < b_index;
12203
0
}
12204
12205
}  // namespace url_pattern_list_detail
12206
12207
template <url_pattern_regex::regex_concept regex_provider>
12208
class url_pattern_list;
12209
12210
/**
12211
 * @private
12212
 * url_pattern_list befriends these two specializations, so they have to be
12213
 * declared before it: a friend declaration that redeclares the function
12214
 * template instead is not matched by every compiler we support (Apple clang
12215
 * 15 rejects the resulting member access). The documented declarations are
12216
 * in implementation.h, next to parse_url_pattern, and carry the default
12217
 * arguments: this header includes url_pattern.h, which includes
12218
 * implementation.h, so those declarations are always seen first, and a
12219
 * function template may not gain default arguments in a later declaration.
12220
 */
12221
template <url_pattern_regex::regex_concept regex_provider>
12222
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12223
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
12224
                       const std::string_view* base_url,
12225
                       const url_pattern_options* options);
12226
12227
/** @private See above. */
12228
template <url_pattern_regex::regex_concept regex_provider>
12229
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12230
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns);
12231
12232
/**
12233
 * @brief A compiled set of URLPattern pathname patterns with one-shot
12234
 * matching.
12235
 *
12236
 * Build once with ada::parse_url_pattern_list, then call match() per
12237
 * request. Matching within the fast-path limits is allocation-free and
12238
 * regex-free for routes written in the static / ":param" / "*" subset; other
12239
 * routes are matched through the regex provider (the same regex_search call
12240
 * url_pattern::exec makes) and still participate in the priority order.
12241
 *
12242
 * Match priority is specificity order, not registration order: routes are
12243
 * compared by their per-segment kind sequence (literal < ":param" < "*",
12244
 * lexicographically from the first segment), with insertion order breaking
12245
 * ties. This is the priority scheme of find-my-way (Fastify) and Express-
12246
 * style routers. Whether a standardized URLPatternList should instead use
12247
 * pure first-match-in-insertion-order semantics is an open question; the
12248
 * compiled representation supports either, and the priority rule is
12249
 * deliberately centralized in url_pattern_list_detail::outranks.
12250
 *
12251
 * Scope (v1): the pathname component only. Other URL components are treated
12252
 * as fully wildcarded; match() takes an already-extracted pathname (for
12253
 * example ada::url_aggregator::get_pathname()). Inputs are matched as given
12254
 * and are expected to be in canonical (percent-encoded) form; the pattern
12255
 * side is canonicalized by the URLPattern pattern parser at creation. The
12256
 * subset follows the URLPattern regexp it stands for: a ":param" segment
12257
 * ("[^/]+?") is one non-empty segment, and a "*" tail ("(.*)") matches any
12258
 * bytes except the line terminators LF and CR, which "." never matches.
12259
 *
12260
 * With url_pattern_options::ignore_case, regexp routes are compiled through
12261
 * the provider with the flag set (as url_pattern does), and the static /
12262
 * ":param" / "*" subset compares literal segments with ASCII case folding,
12263
 * which is what a case-insensitive regular expression does over the ASCII
12264
 * text of a canonical pathname.
12265
 *
12266
 * @tparam regex_provider The regex implementation used only for routes that
12267
 *         need URLPattern regexp semantics. Must satisfy
12268
 *         url_pattern_regex::regex_concept.
12269
 */
12270
template <url_pattern_regex::regex_concept regex_provider>
12271
class url_pattern_list {
12272
 public:
12273
  url_pattern_list() = default;
12274
12275
  /**
12276
   * Matches a pathname against the set and returns the winning route with
12277
   * its captures (see url_pattern_list_match_result). The fast path is
12278
   * allocation-free; inputs beyond the fast-path limits are matched by the
12279
   * sequential fallback with identical semantics.
12280
   */
12281
  [[nodiscard]] url_pattern_list_match_result match(
12282
      std::string_view pathname) const;
12283
12284
  /** Number of routes in the set. */
12285
  [[nodiscard]] size_t size() const noexcept { return patterns_.size(); }
12286
12287
  /** The pathname pattern string the route at `route_index` was created
12288
   * from (after base URL processing, if a base URL was given). */
12289
  [[nodiscard]] std::string_view pattern(size_t route_index) const
12290
      ada_lifetime_bound {
12291
    return patterns_[route_index];
12292
  }
12293
12294
  /**
12295
   * Capture group names of the route at `route_index`, in the order of
12296
   * url_pattern_list_match_result::captures / regexp_groups ("*" groups have
12297
   * numeric names, per URLPattern).
12298
   */
12299
  [[nodiscard]] const std::vector<std::string>& group_names(
12300
      size_t route_index) const ada_lifetime_bound {
12301
    return compiled_.group_names[route_index];
12302
  }
12303
12304
  /** The ignore_case option the set was created with. */
12305
  [[nodiscard]] bool ignore_case() const noexcept {
12306
    return compiled_.ignore_case != 0;
12307
  }
12308
12309
  friend tl::expected<url_pattern_list, errors>
12310
  parse_url_pattern_list<regex_provider>(
12311
      std::span<const std::string_view> pathname_patterns,
12312
      const std::string_view* base_url, const url_pattern_options* options);
12313
12314
  friend tl::expected<url_pattern_list, errors> parse_url_pattern_list<
12315
      regex_provider>(std::span<const url_pattern<regex_provider>> patterns);
12316
12317
 private:
12318
  /** @private Compiles the (already processed) pattern strings; the regexp
12319
   * components are then supplied by the caller in insertion order. */
12320
  static tl::expected<url_pattern_list, errors> create(
12321
      std::vector<std::string>&& pathname_patterns, bool ignore_case);
12322
12323
  /** @private Tests one auxiliary route (sequential or regexp mode) and,
12324
   * when it matches and outranks `best`, replaces `best` with it. `probe`
12325
   * is the (possibly case-folded) pathname for the sequential matcher,
12326
   * `pathname` the original for the provider. */
12327
  void consider_route(
12328
      uint32_t route_index, std::string_view pathname, std::string_view probe,
12329
      bool fold_input, url_pattern_list_detail::engine_result& best,
12330
      std::vector<std::optional<std::string>>& best_groups) const;
12331
12332
  /** @private The pattern strings, by route index. */
12333
  std::vector<std::string> patterns_{};
12334
  /** @private The compiled tables. */
12335
  url_pattern_list_detail::compiled_routes compiled_{};
12336
  /** @private Compiled pathname components of the regexp-mode routes, in
12337
   * insertion order. */
12338
  std::vector<url_pattern_component<regex_provider>> regexp_components_{};
12339
};
12340
12341
}  // namespace ada
12342
#endif  // ADA_INCLUDE_URL_PATTERN
12343
#endif  // ADA_URL_PATTERN_LIST_H
12344
/* end file include/ada/url_pattern_list.h */
12345
/* begin file include/ada/url_pattern_list-inl.h */
12346
/**
12347
 * @file url_pattern_list-inl.h
12348
 * @brief The url_pattern_list matcher (inline) and the class template's
12349
 * member definitions.
12350
 *
12351
 * The walk over the compiled tables lives here, as ada_really_inline
12352
 * functions, so that it inlines into url_pattern_list::match; the route-set
12353
 * compiler that builds the tables is in its own translation unit
12354
 * (src/url_pattern_list.cpp) and is not part of the public headers.
12355
 */
12356
#ifndef ADA_URL_PATTERN_LIST_INL_H
12357
#define ADA_URL_PATTERN_LIST_INL_H
12358
12359
12360
#include <bit>
12361
#include <cstdint>
12362
#include <cstring>
12363
#include <optional>
12364
#include <string>
12365
#include <string_view>
12366
#include <utility>
12367
#include <vector>
12368
12369
#if ADA_NEON
12370
#include <arm_neon.h>
12371
#endif
12372
12373
#if ADA_INCLUDE_URL_PATTERN
12374
namespace ada::url_pattern_list_detail {
12375
12376
// ---- byte helpers ----------------------------------------------------------
12377
12378
// Unaligned 8-byte little-endian load; all 8 bytes must be readable. The
12379
// byte-assembly fallback keeps big-endian targets correct (the compiler
12380
// lowers it to a single load plus byte swap).
12381
0
ada_really_inline uint64_t load8_le(const char* p) noexcept {
12382
0
  if constexpr (std::endian::native == std::endian::little) {
12383
0
    uint64_t x;
12384
0
    std::memcpy(&x, p, 8);
12385
0
    return x;
12386
  } else {
12387
    uint64_t x = 0;
12388
    for (uint32_t j = 0; j < 8; j++) {
12389
      x |= static_cast<uint64_t>(static_cast<uint8_t>(p[j])) << (8 * j);
12390
    }
12391
    return x;
12392
  }
12393
0
}
12394
12395
// Bytes [0, n) of p packed little-endian, zero-padded; reads exactly n
12396
// bytes (n < 8). This is the byte-compare path for short tails.
12397
0
ada_really_inline uint64_t gather_le(const char* p, uint32_t n) noexcept {
12398
0
  uint64_t x = 0;
12399
0
  for (uint32_t j = 0; j < n; j++) {
12400
0
    x |= static_cast<uint64_t>(static_cast<uint8_t>(p[j])) << (8 * j);
12401
0
  }
12402
0
  return x;
12403
0
}
12404
12405
// Mask selecting the low n bytes, 1 <= n <= 7.
12406
0
constexpr uint64_t low_bytes_mask(uint32_t n) noexcept {
12407
0
  return ~0ull >> (64 - 8 * n);
12408
0
}
12409
12410
// ASCII case folding of n bytes from src into dst (the compiler vectorizes
12411
// this loop). Only ASCII letters change: canonical pathnames are ASCII, and
12412
// this is what a case-insensitive regular expression does over them.
12413
0
inline void ascii_fold(const char* src, uint32_t n, char* dst) noexcept {
12414
0
  for (uint32_t i = 0; i < n; i++) {
12415
0
    const uint8_t c = static_cast<uint8_t>(src[i]);
12416
0
    dst[i] = static_cast<char>(c | ((c >= 'A' && c <= 'Z') ? 0x20u : 0u));
12417
0
  }
12418
0
}
12419
12420
// ---- witness machinery -----------------------------------------------------
12421
// Projection dispatch for wide trie nodes. A witness id names a feature of a
12422
// byte string:
12423
//   0..7   the byte at absolute offset id           (0 when out of range)
12424
//   8..15  the byte at offset id-8 counted from the end
12425
// A projection is three witness bytes plus the length packed into one
12426
// uint64; a perfect multiplier turns it into a slot index. Builder and
12427
// matcher call the same accessors and the same packing, so a slot table
12428
// cannot disagree with the gather that reads it.
12429
12430
0
constexpr uint8_t length_byte(uint32_t len) noexcept {
12431
0
  return len < 255u ? static_cast<uint8_t>(len) : static_cast<uint8_t>(255);
12432
0
}
12433
12434
constexpr uint8_t witness_byte(const char* p, uint32_t len,
12435
0
                               uint8_t id) noexcept {  // ids 0..15
12436
0
  uint32_t o = id & 7u;
12437
0
  bool in = o < len;
12438
0
  uint32_t pos = (id & 8u) ? len - 1u - o : o;  // wraps when !in; masked then
12439
0
  pos = in ? pos : 0u;
12440
0
  uint8_t v = len ? static_cast<uint8_t>(p[pos]) : static_cast<uint8_t>(0);
12441
0
  return in ? v : static_cast<uint8_t>(0);
12442
0
}
12443
12444
constexpr uint64_t pack_projection(uint8_t f0, uint8_t f1, uint8_t f2,
12445
0
                                   uint8_t f3) noexcept {
12446
0
  return (static_cast<uint64_t>(f0) << 24) | (static_cast<uint64_t>(f1) << 16) |
12447
0
         (static_cast<uint64_t>(f2) << 8) | static_cast<uint64_t>(f3);
12448
0
}
12449
12450
// Perfect-multiplier slot index; b <= 12 everywhere, so the shift is in
12451
// range and the product needs no modulus.
12452
constexpr uint64_t slot_of(uint64_t proj, uint64_t multiplier,
12453
0
                           uint8_t b) noexcept {
12454
0
  return (proj * multiplier) >> (64 - b);
12455
0
}
12456
12457
// Fixed-shape projection of one string: always 3 witness bytes plus the
12458
// length. Duplicated ids add no information but keep the gather uniform.
12459
constexpr uint64_t project(const char* p, uint32_t len,
12460
0
                           uint16_t plan) noexcept {
12461
0
  return pack_projection(
12462
0
      witness_byte(p, len, static_cast<uint8_t>(plan & 15u)),
12463
0
      witness_byte(p, len, static_cast<uint8_t>((plan >> 4) & 15u)),
12464
0
      witness_byte(p, len, static_cast<uint8_t>((plan >> 8) & 15u)),
12465
0
      length_byte(len));
12466
0
}
12467
12468
// ---- segment scan ----------------------------------------------------------
12469
12470
// Splits the pathname into segments in one pass, in place: only segment
12471
// starts are recorded, with a sentinel soff[nseg] = ulen + 1 so that
12472
// seg_len(i) == soff[i + 1] - soff[i] - 1. Returns the segment count, or 0
12473
// when the input has more than max_fast_path_segments segments (the caller
12474
// falls back to the sequential matcher). Requires ulen >= 1 and
12475
// url[0] == '/'.
12476
//
12477
// Two implementations with the same contract: the portable SWAR scan (8
12478
// bytes per step, an exact zero-byte test on x ^ '/'-fill with no false
12479
// positives, bytes >= 0x80 included, and a byte-exact partial load for the
12480
// tail, so the input is never over-read), and on AArch64 a NEON scan for
12481
// inputs of 16 bytes or more (16 bytes per step, the '/' compare narrowed
12482
// to one nibble per byte, the last block overlapping the previous one so
12483
// the input is never over-read). The 1k+ pathname benchmark in the PR
12484
// discussion is what keeps the NEON scan: it is faster at every length and
12485
// 3-4x faster past 1 KB.
12486
struct segment_emitter {
12487
  uint16_t* soff;
12488
  uint32_t nseg = 0;
12489
  uint32_t start = 1;
12490
  // One segment start per set bit of `lanes`; a lane is 1 << shift bits
12491
  // wide (8 for the SWAR masks, 4 for the NEON nibble masks).
12492
  ada_really_inline bool emit(uint64_t lanes, uint32_t base,
12493
0
                              unsigned shift) noexcept {
12494
0
    using url_pattern_list_limits::max_fast_path_segments;
12495
0
    while (lanes) {
12496
0
      const uint32_t pos =
12497
0
          base + (static_cast<uint32_t>(std::countr_zero(lanes)) >> shift);
12498
0
      if (nseg >= max_fast_path_segments) {
12499
0
        return false;
12500
0
      }
12501
0
      soff[nseg++] = static_cast<uint16_t>(start);
12502
0
      start = pos + 1;
12503
0
      lanes &= lanes - 1;
12504
0
    }
12505
0
    return true;
12506
0
  }
12507
0
  ada_really_inline uint32_t finish(uint32_t ulen) noexcept {
12508
0
    using url_pattern_list_limits::max_fast_path_segments;
12509
0
    if (nseg >= max_fast_path_segments) {
12510
0
      return 0;
12511
0
    }
12512
0
    soff[nseg++] = static_cast<uint16_t>(start);
12513
0
    soff[nseg] = static_cast<uint16_t>(ulen + 1);  // sentinel
12514
0
    return nseg;
12515
0
  }
12516
};
12517
12518
ada_really_inline uint32_t scan_segments_swar(const char* url, uint32_t ulen,
12519
0
                                              uint16_t* soff) noexcept {
12520
0
  constexpr uint64_t slashes = 0x2F2F2F2F2F2F2F2Full;
12521
0
  constexpr uint64_t low7 = 0x7F7F7F7F7F7F7F7Full;
12522
0
  // Bit 7 of every byte of x that is zero, exactly: (b & 0x7F) + 0x7F has
12523
0
  // bit 7 set iff the low bits are non-zero, b itself has it set iff b is
12524
0
  // >= 0x80, and no lane can carry into its neighbour.
12525
0
  const auto zero_lanes = [](uint64_t x) noexcept {
12526
0
    return ~(((x & low7) + low7) | x | low7);
12527
0
  };
12528
0
  segment_emitter out{soff};
12529
0
  uint32_t i = 1;
12530
0
  for (; i + 8 <= ulen; i += 8) {
12531
0
    const uint64_t lanes = zero_lanes(load8_le(url + i) ^ slashes);
12532
0
    if (lanes != 0 && !out.emit(lanes, i, 3)) {
12533
0
      return 0;
12534
0
    }
12535
0
  }
12536
0
  if (i < ulen) {  // tail of 1..7 bytes: zero padding is never a '/'
12537
0
    const uint64_t lanes = zero_lanes(gather_le(url + i, ulen - i) ^ slashes);
12538
0
    if (lanes != 0 && !out.emit(lanes, i, 3)) {
12539
0
      return 0;
12540
0
    }
12541
0
  }
12542
0
  return out.finish(ulen);
12543
0
}
12544
12545
#if ADA_NEON
12546
ada_really_inline uint32_t scan_segments_neon(const char* url, uint32_t ulen,
12547
                                              uint16_t* soff) noexcept {
12548
  // Requires ulen >= 16.
12549
  const uint8x16_t slash = vdupq_n_u8('/');
12550
  // One bit per byte lane that holds a '/': compare, narrow each 16-bit
12551
  // pair to its high nibble, keep bit 0 of every nibble.
12552
  const auto slash_lanes = [&](const char* p) noexcept {
12553
    const uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
12554
    const uint64_t nibbles =
12555
        vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(
12556
                          vreinterpretq_u16_u8(vceqq_u8(v, slash)), 4)),
12557
                      0);
12558
    return nibbles & 0x1111111111111111ull;
12559
  };
12560
  segment_emitter out{soff};
12561
  uint32_t i = 0;
12562
  for (; i + 16 <= ulen; i += 16) {
12563
    uint64_t lanes = slash_lanes(url + i);
12564
    if (i == 0) {
12565
      lanes &= ~0xFull;  // the leading '/' is not a separator
12566
    }
12567
    if (lanes != 0 && !out.emit(lanes, i, 2)) {
12568
      return 0;
12569
    }
12570
  }
12571
  if (i < ulen) {  // overlapped last block: drop the lanes already scanned
12572
    const uint32_t off = ulen - 16;
12573
    const uint64_t lanes = slash_lanes(url + off) & (~0ull << ((i - off) * 4));
12574
    if (lanes != 0 && !out.emit(lanes, off, 2)) {
12575
      return 0;
12576
    }
12577
  }
12578
  return out.finish(ulen);
12579
}
12580
#endif  // ADA_NEON
12581
12582
ada_really_inline uint32_t scan_segments(const char* url, uint32_t ulen,
12583
0
                                         uint16_t* soff) noexcept {
12584
0
#if ADA_NEON
12585
0
  if (ulen >= 16) {
12586
0
    return scan_segments_neon(url, ulen, soff);
12587
0
  }
12588
0
#endif
12589
0
  return scan_segments_swar(url, ulen, soff);
12590
0
}
12591
12592
// ---- verify / dispatch -----------------------------------------------------
12593
12594
// One input segment prepared for dispatch: its bytes, its length, and how
12595
// many bytes are readable from p (to the end of the pathname).
12596
struct segment_ref {
12597
  const char* p;
12598
  uint32_t len;
12599
  uint32_t avail;
12600
};
12601
12602
// Segment-vs-edge compare. Keys of 8..16 bytes verify from two whole-word
12603
// loads inside the segment; shorter keys from one masked load when the
12604
// input has 8 readable bytes, else from a byte gather (short tails such as
12605
// "42" or "me"); longer keys through memcmp against the blob. An empty key
12606
// (a pattern such as "/users/") matches exactly the empty segment.
12607
ada_really_inline bool verify_edge(const edge_record& e, const segment_ref& s,
12608
0
                                   const char* blob) noexcept {
12609
0
  const uint32_t len = s.len;
12610
0
  if (len != e.key_length) {
12611
0
    return false;
12612
0
  }
12613
0
  if (len == 0) {
12614
0
    return true;
12615
0
  }
12616
0
  if (len > 16) {
12617
0
    return std::memcmp(s.p, blob + e.key_offset, len) == 0;
12618
0
  }
12619
0
  if (len >= 8) {
12620
0
    return ((load8_le(s.p) ^ e.prefix) |
12621
0
            (load8_le(s.p + len - 8) ^ e.suffix)) == 0;
12622
0
  }
12623
0
  const uint64_t x = s.avail >= 8 ? (load8_le(s.p) & low_bytes_mask(len))
12624
0
                                  : gather_le(s.p, len);
12625
0
  return x == e.prefix;
12626
0
}
12627
12628
// The typed view of the arena the walk reads; derived once per match.
12629
struct table_view {
12630
  const node_record* nodes;
12631
  const hash_record* hashes;
12632
  const edge_record* edges;
12633
  const uint8_t* slots;
12634
  const char* blob;
12635
  const uint16_t* root_index;
12636
};
12637
12638
0
ada_really_inline table_view view_of(const compiled_routes& r) noexcept {
12639
0
  return table_view{r.section<node_record>(r.nodes_offset),
12640
0
                    r.section<hash_record>(r.hashes_offset),
12641
0
                    r.section<edge_record>(r.edges_offset),
12642
0
                    r.section<uint8_t>(r.slots_offset),
12643
0
                    r.section<char>(r.blob_offset),
12644
0
                    r.section<uint16_t>(r.root_index_offset)};
12645
0
}
12646
12647
// Static-child dispatch: returns the child ordinal within the node or -1.
12648
// Correctness never depends on the projection or the index: the candidate
12649
// is always confirmed by a full segment compare, so a bad table can only
12650
// cost time, never change an answer.
12651
ada_really_inline int32_t dispatch_static(const table_view& t,
12652
                                          const node_record& nd,
12653
0
                                          const segment_ref& s) noexcept {
12654
0
  const edge_record* e = t.edges + nd.first_child;
12655
0
  switch (nd.dispatch) {
12656
0
    case 0:
12657
0
      return -1;
12658
0
    case 1: {  // direct: up to 8 compares, each gated by the key length
12659
0
      for (uint32_t j = 0; j < nd.n_static; j++) {
12660
0
        if (verify_edge(e[j], s, t.blob)) {
12661
0
          return static_cast<int32_t>(j);
12662
0
        }
12663
0
      }
12664
0
      return -1;
12665
0
    }
12666
0
    case 2: {  // projection: gather -> multiply -> slot -> verify
12667
0
      const hash_record& h = t.hashes[nd.hash_index];
12668
0
      const uint64_t proj = project(s.p, s.len, h.witness_plan);
12669
0
      const uint8_t ord =
12670
0
          t.slots[h.slot_base + slot_of(proj, h.multiplier, h.slot_bits)];
12671
0
      if (ord == 0xFF) {
12672
0
        return -1;
12673
0
      }
12674
0
      return verify_edge(e[ord], s, t.blob) ? static_cast<int32_t>(ord) : -1;
12675
0
    }
12676
0
    case 4: {  // root first-byte index: jump to the run of children that
12677
0
               // share the segment's first byte (children are sorted by it)
12678
0
      if (s.len == 0) {
12679
0
        return -1;  // no first byte; an indexed root has no empty key
12680
0
      }
12681
0
      const uint8_t b = static_cast<uint8_t>(s.p[0]);
12682
0
      uint32_t j = t.root_index[b];
12683
0
      if (j == 0xFFFF) {
12684
0
        return -1;
12685
0
      }
12686
0
      for (; j < nd.n_static && static_cast<uint8_t>(e[j].prefix & 0xFFu) == b;
12687
0
           j++) {
12688
0
        if (verify_edge(e[j], s, t.blob)) {
12689
0
          return static_cast<int32_t>(j);
12690
0
        }
12691
0
      }
12692
0
      return -1;
12693
0
    }
12694
0
    default: {  // linear demotion rung (also carries fanout > 254)
12695
0
      for (uint32_t j = 0; j < nd.n_static; j++) {
12696
0
        if (verify_edge(e[j], s, t.blob)) {
12697
0
          return static_cast<int32_t>(j);
12698
0
        }
12699
0
      }
12700
0
      return -1;
12701
0
    }
12702
0
  }
12703
0
}
12704
12705
// ---- the match residual ----------------------------------------------------
12706
12707
// Matches `pathname` against the compiled tables. When the input is out of
12708
// the fast-path contract, returns with within_fast_path == false and no
12709
// answer; the caller falls back to the sequential matcher.
12710
ada_really_inline void match_compiled(const compiled_routes& r, const char* url,
12711
                                      uint32_t ulen,
12712
0
                                      engine_result& out) noexcept {
12713
0
  using url_pattern_list_limits::max_fast_path_pathname_length;
12714
0
  using url_pattern_list_limits::max_fast_path_segments;
12715
0
  out = engine_result{};
12716
0
  if (ulen == 0 || url[0] != '/' || ulen > max_fast_path_pathname_length) {
12717
0
    out.within_fast_path = false;  // the sequential fallback decides
12718
0
    return;
12719
0
  }
12720
0
  const table_view t = view_of(r);
12721
0
  const route_record* routes = r.section<route_record>(r.routes_offset);
12722
0
12723
0
  // Segment scan; overflow means the input is beyond the fast path.
12724
0
  uint16_t soff[max_fast_path_segments + 1];
12725
0
  const uint32_t nseg = scan_segments(url, ulen, soff);
12726
0
  if (nseg == 0) {
12727
0
    out.within_fast_path = false;
12728
0
    return;
12729
0
  }
12730
0
  const auto seg_len = [&](uint32_t i) {
12731
0
    return static_cast<uint32_t>(soff[i + 1]) - soff[i] - 1u;
12732
0
  };
12733
0
  const auto seg_ref = [&](uint32_t i) {
12734
0
    const uint32_t off = soff[i];
12735
0
    return segment_ref{url + off, seg_len(i), ulen - off};
12736
0
  };
12737
0
12738
0
  // Trie walk with an explicit bounded backtrack stack. Every node offers
12739
0
  // the same three alternatives in the compiled priority order (static
12740
0
  // child, param child, wildcard); a backtrack record stores the resume
12741
0
  // point, and a record is pushed only when the node still has an untried
12742
0
  // alternative, so the stack never exceeds the walk depth. Edges encoded
12743
0
  // as -2 - route are pure-leaf shortcuts.
12744
0
  enum alternative : uint16_t {
12745
0
    alt_static = 0,
12746
0
    alt_param = 1,
12747
0
    alt_wild = 2,
12748
0
    alt_none = 3
12749
0
  };
12750
0
  struct backtrack {
12751
0
    int32_t node;
12752
0
    uint16_t i;
12753
0
    uint16_t resume;
12754
0
  };
12755
0
  backtrack stack[max_fast_path_segments + 1];
12756
0
  uint32_t sp = 0;
12757
0
  int32_t node = 0;
12758
0
  uint32_t i = 0;
12759
0
  uint32_t alt = alt_static;
12760
0
  int32_t route = -1;
12761
0
  uint32_t wild_from = 0;
12762
0
  bool wild = false;
12763
0
  for (;;) {
12764
0
    const node_record& nd = t.nodes[static_cast<size_t>(node)];
12765
0
    if (alt == alt_static) {
12766
0
      if (i == nseg) {
12767
0
        if (nd.terminal_route >= 0) {
12768
0
          route = nd.terminal_route;
12769
0
          break;
12770
0
        }
12771
0
        alt = alt_none;
12772
0
      } else {
12773
0
        const int32_t ord = dispatch_static(t, nd, seg_ref(i));
12774
0
        if (ord >= 0) {
12775
0
          const int32_t nxt = t.edges[static_cast<size_t>(nd.first_child) +
12776
0
                                      static_cast<size_t>(ord)]
12777
0
                                  .node;
12778
0
          if (nxt >= 0) {
12779
0
            if (nd.has_alternative) {
12780
0
              stack[sp++] = {node, static_cast<uint16_t>(i), alt_param};
12781
0
            }
12782
0
            node = nxt;
12783
0
            i++;
12784
0
            continue;
12785
0
          }
12786
0
          // Pure-leaf shortcut.
12787
0
          if (i + 1 == nseg) {
12788
0
            route = -2 - nxt;
12789
0
            break;
12790
0
          }
12791
0
          alt = alt_param;  // dead end: fall through to this node's fallbacks
12792
0
        } else {
12793
0
          alt = alt_param;
12794
0
        }
12795
0
      }
12796
0
    }
12797
0
    if (alt == alt_param) {
12798
0
      const int32_t pc = nd.param_child;
12799
0
      if (pc != -1 && seg_len(i) > 0) {
12800
0
        if (pc >= 0) {
12801
0
          if (nd.wild_route >= 0) {
12802
0
            stack[sp++] = {node, static_cast<uint16_t>(i), alt_wild};
12803
0
          }
12804
0
          node = pc;
12805
0
          i++;
12806
0
          alt = alt_static;
12807
0
          continue;
12808
0
        }
12809
0
        // Pure-leaf param shortcut.
12810
0
        if (i + 1 == nseg) {
12811
0
          route = -2 - pc;
12812
0
          break;
12813
0
        }
12814
0
        alt = alt_wild;  // only the wildcard remains here
12815
0
      } else {
12816
0
        alt = alt_wild;
12817
0
      }
12818
0
    }
12819
0
    if (alt == alt_wild) {
12820
0
      if (nd.wild_route >= 0 &&
12821
0
          wildcard_tail_ok(url + soff[i], ulen - soff[i])) {
12822
0
        route = nd.wild_route;
12823
0
        wild_from = i;
12824
0
        wild = true;
12825
0
        break;
12826
0
      }
12827
0
      // No alternative left at this node: resume from the backtrack stack
12828
0
      // (alt is overwritten by the popped record).
12829
0
    }
12830
0
    if (sp == 0) {
12831
0
      break;  // nothing to resume: miss
12832
0
    }
12833
0
    --sp;
12834
0
    node = stack[sp].node;
12835
0
    i = stack[sp].i;
12836
0
    alt = stack[sp].resume;
12837
0
  }
12838
0
12839
0
  out.route = route;
12840
0
  if (route >= 0) {  // capture extraction: params first, wildcard tail last
12841
0
    const route_record& rm = routes[static_cast<size_t>(route)];
12842
0
    uint32_t nc = 0;
12843
0
    for (uint32_t k = 0; k < rm.n_params; k++) {
12844
0
      const uint32_t p = rm.param_positions[k];
12845
0
      out.captures[nc++] = {soff[p], seg_len(p)};
12846
0
    }
12847
0
    if (wild) {
12848
0
      out.captures[nc++] = {soff[wild_from], ulen - soff[wild_from]};
12849
0
    }
12850
0
    out.capture_count = nc;
12851
0
  }
12852
0
}
12853
12854
}  // namespace ada::url_pattern_list_detail
12855
12856
namespace ada {
12857
12858
template <url_pattern_regex::regex_concept regex_provider>
12859
tl::expected<url_pattern_list<regex_provider>, errors>
12860
url_pattern_list<regex_provider>::create(
12861
    std::vector<std::string>&& pathname_patterns, bool ignore_case) {
12862
  url_pattern_list list{};
12863
  list.patterns_ = std::move(pathname_patterns);
12864
  auto compiled = url_pattern_list_detail::compile_pathname_patterns(
12865
      list.patterns_, ignore_case);
12866
  if (!compiled) {
12867
    return tl::unexpected(compiled.error());
12868
  }
12869
  list.compiled_ = std::move(*compiled);
12870
  return list;
12871
}
12872
12873
template <url_pattern_regex::regex_concept regex_provider>
12874
void url_pattern_list<regex_provider>::consider_route(
12875
    uint32_t route_index, std::string_view pathname, std::string_view probe,
12876
    bool fold_input, url_pattern_list_detail::engine_result& best,
12877
    std::vector<std::optional<std::string>>& best_groups) const {
12878
  namespace detail = url_pattern_list_detail;
12879
  const detail::route_record* routes =
12880
      compiled_.section<detail::route_record>(compiled_.routes_offset);
12881
  const detail::route_record& candidate = routes[route_index];
12882
  // Only test candidates that would outrank the current best.
12883
  if (best.route >= 0) {
12884
    const detail::route_record& current =
12885
        routes[static_cast<size_t>(best.route)];
12886
    if (!detail::outranks(candidate.kind_sequence, candidate.kind_length,
12887
                          route_index, current.kind_sequence,
12888
                          current.kind_length,
12889
                          static_cast<size_t>(best.route))) {
12890
      return;
12891
    }
12892
  }
12893
  if (candidate.mode == detail::route_mode::regexp) {
12894
    // A route whose certain segment shape does not fit the input cannot
12895
    // match at all: skip the provider.
12896
    if (!detail::match_regexp_shape(compiled_, route_index, probe,
12897
                                    fold_input)) {
12898
      return;
12899
    }
12900
    // URLPattern's own test/exec split: regex_match answers yes/no (this is
12901
    // what rejects a non-matching route cheaply; a regex_search miss can
12902
    // cost a scan over every start position), and regex_search then
12903
    // produces the group values exactly as url_pattern::exec does.
12904
    const url_pattern_component<regex_provider>& component =
12905
        regexp_components_[static_cast<size_t>(candidate.regexp_component)];
12906
    if (!component.fast_test(pathname)) {
12907
      return;
12908
    }
12909
    auto groups = component.fast_match(pathname);
12910
    if (groups) {
12911
      best = detail::engine_result{};
12912
      best.route = static_cast<int32_t>(route_index);
12913
      best_groups = std::move(*groups);
12914
    }
12915
  } else {
12916
    detail::engine_result scratch{};
12917
    if (detail::match_route_sequential(compiled_, route_index, probe,
12918
                                       fold_input, scratch)) {
12919
      scratch.route = static_cast<int32_t>(route_index);
12920
      best = scratch;
12921
      best_groups.clear();
12922
    }
12923
  }
12924
}
12925
12926
template <url_pattern_regex::regex_concept regex_provider>
12927
url_pattern_list_match_result url_pattern_list<regex_provider>::match(
12928
    std::string_view pathname) const {
12929
  namespace detail = url_pattern_list_detail;
12930
  using url_pattern_list_limits::max_fast_path_pathname_length;
12931
  // A default-constructed list (or one compiled from no routes) has no
12932
  // tables. Walking them would dereference section pointers into an empty
12933
  // arena.
12934
  if (patterns_.empty() || compiled_.arena.empty()) {
12935
    return {};
12936
  }
12937
  // With ignore_case the compiled literals are ASCII-folded, so the fast
12938
  // path probes a folded copy of the input (offsets are unchanged, so the
12939
  // captures still slice the original). Inputs beyond the fast-path length
12940
  // are folded on the fly by the sequential matcher instead.
12941
  char folded[max_fast_path_pathname_length];
12942
  std::string_view probe = pathname;
12943
  bool fold_input = false;
12944
  if (compiled_.ignore_case) {
12945
    if (pathname.size() <= max_fast_path_pathname_length) {
12946
      detail::ascii_fold(pathname.data(),
12947
                         static_cast<uint32_t>(pathname.size()), folded);
12948
      probe = std::string_view(folded, pathname.size());
12949
    } else {
12950
      fold_input = true;
12951
    }
12952
  }
12953
  detail::engine_result best{};
12954
  std::vector<std::optional<std::string>> best_groups{};
12955
  detail::match_compiled(compiled_, probe.data(),
12956
                         static_cast<uint32_t>(probe.size()), best);
12957
  const uint32_t* aux = compiled_.section<uint32_t>(compiled_.aux_offset);
12958
  if (!best.within_fast_path) {
12959
    // The input exceeds a fast-path limit (length, segment count, or no
12960
    // leading '/'): the sequential fallback matches every route with
12961
    // identical priority semantics.
12962
    best = detail::engine_result{};
12963
    for (size_t i = 0; i < patterns_.size(); i++) {
12964
      consider_route(static_cast<uint32_t>(i), pathname, probe, fold_input,
12965
                     best, best_groups);
12966
    }
12967
  } else if (best.route >= 0) {
12968
    // A fast-path winner is final except for the auxiliary routes recorded
12969
    // at creation as able to outrank it (usually none).
12970
    const detail::route_record& winner =
12971
        compiled_.section<detail::route_record>(
12972
            compiled_.routes_offset)[static_cast<size_t>(best.route)];
12973
    for (uint32_t k = 0; k < winner.challenger_count; k++) {
12974
      consider_route(aux[winner.challenger_first + k], pathname, probe,
12975
                     fold_input, best, best_groups);
12976
    }
12977
  } else {
12978
    // A fast-path miss: any auxiliary route may still match.
12979
    for (uint32_t k = 0; k < compiled_.n_aux_all; k++) {
12980
      consider_route(aux[k], pathname, probe, fold_input, best, best_groups);
12981
    }
12982
  }
12983
  url_pattern_list_match_result result{};
12984
  result.route_index = best.route;
12985
  if (best.route >= 0) {
12986
    const detail::route_record& winner =
12987
        compiled_.section<detail::route_record>(
12988
            compiled_.routes_offset)[static_cast<size_t>(best.route)];
12989
    if (winner.mode == detail::route_mode::regexp) {
12990
      result.regexp_route = true;
12991
      result.regexp_groups = std::move(best_groups);
12992
    } else {
12993
      result.capture_count = best.capture_count;
12994
      result.captures_truncated = best.captures_truncated;
12995
      result.captures = best.captures;
12996
    }
12997
  }
12998
  return result;
12999
}
13000
13001
}  // namespace ada
13002
#endif  // ADA_INCLUDE_URL_PATTERN
13003
#endif  // ADA_URL_PATTERN_LIST_INL_H
13004
/* end file include/ada/url_pattern_list-inl.h */
13005
13006
// Public API
13007
/* begin file include/ada/ada_version.h */
13008
/**
13009
 * @file ada_version.h
13010
 * @brief Definitions for Ada's version number.
13011
 */
13012
#ifndef ADA_ADA_VERSION_H
13013
#define ADA_ADA_VERSION_H
13014
13015
0
#define ADA_VERSION "4.0.0"
13016
13017
namespace ada {
13018
13019
enum {
13020
  ADA_VERSION_MAJOR = 4,
13021
  ADA_VERSION_MINOR = 0,
13022
  ADA_VERSION_REVISION = 0,
13023
};
13024
13025
}  // namespace ada
13026
13027
#endif  // ADA_ADA_VERSION_H
13028
/* end file include/ada/ada_version.h */
13029
/* begin file include/ada/implementation-inl.h */
13030
/**
13031
 * @file implementation-inl.h
13032
 */
13033
#ifndef ADA_IMPLEMENTATION_INL_H
13034
#define ADA_IMPLEMENTATION_INL_H
13035
13036
13037
13038
#include <cstddef>
13039
#include <span>
13040
#include <string>
13041
#include <string_view>
13042
#include <utility>
13043
#include <variant>
13044
#include <vector>
13045
13046
namespace ada {
13047
13048
#if ADA_INCLUDE_URL_PATTERN
13049
template <url_pattern_regex::regex_concept regex_provider>
13050
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
13051
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
13052
                  const std::string_view* base_url,
13053
                  const url_pattern_options* options) {
13054
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
13055
                                                        base_url, options);
13056
}
13057
13058
template <url_pattern_regex::regex_concept regex_provider>
13059
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
13060
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
13061
                       const std::string_view* base_url,
13062
                       const url_pattern_options* options) {
13063
  std::vector<std::string> processed;
13064
  processed.reserve(pathname_patterns.size());
13065
  for (const std::string_view pattern : pathname_patterns) {
13066
    if (base_url == nullptr) {
13067
      processed.emplace_back(pattern);
13068
      continue;
13069
    }
13070
    // With a base URL, each pattern is processed exactly as the pathname of
13071
    // a URLPatternInit would be: a relative pattern is resolved against the
13072
    // base URL's path (and an unparsable base URL is a type error).
13073
    url_pattern_init init{};
13074
    init.pathname = std::string(pattern);
13075
    init.base_url = std::string(*base_url);
13076
    auto result = url_pattern_init::process(
13077
        init, url_pattern_init::process_type::pattern);
13078
    if (!result) {
13079
      return tl::unexpected(result.error());
13080
    }
13081
    processed.push_back(std::move(result->pathname).value_or(std::string{}));
13082
  }
13083
  const bool ignore_case = options != nullptr && options->ignore_case;
13084
  auto list = url_pattern_list<regex_provider>::create(std::move(processed),
13085
                                                       ignore_case);
13086
  if (!list) {
13087
    return list;
13088
  }
13089
  // Routes outside the static/":param"/"*" subset are compiled as a
13090
  // URLPattern pathname component through the provider, with the same
13091
  // options parse_url_pattern would use (ignore_case included).
13092
  auto compile_options = url_pattern_compile_component_options::PATHNAME;
13093
  compile_options.ignore_case = ignore_case;
13094
  const url_pattern_list_detail::route_record* routes =
13095
      list->compiled_.template section<url_pattern_list_detail::route_record>(
13096
          list->compiled_.routes_offset);
13097
  for (size_t i = 0; i < list->patterns_.size(); i++) {
13098
    if (routes[i].regexp_component < 0) {
13099
      continue;
13100
    }
13101
    auto component = url_pattern_component<regex_provider>::compile(
13102
        list->patterns_[i], url_pattern_helpers::canonicalize_pathname,
13103
        compile_options);
13104
    if (!component) {
13105
      return tl::unexpected(component.error());
13106
    }
13107
    list->compiled_.group_names[i] = component->group_name_list;
13108
    list->regexp_components_.push_back(std::move(*component));
13109
  }
13110
  return list;
13111
}
13112
13113
template <url_pattern_regex::regex_concept regex_provider>
13114
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
13115
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns) {
13116
  std::vector<std::string> texts;
13117
  texts.reserve(patterns.size());
13118
  const bool ignore_case = !patterns.empty() && patterns[0].ignore_case();
13119
  for (const url_pattern<regex_provider>& pattern : patterns) {
13120
    if (pattern.ignore_case() != ignore_case) {
13121
      return tl::unexpected(errors::type_error);  // one flag per list
13122
    }
13123
    texts.emplace_back(pattern.get_pathname());
13124
  }
13125
  auto list =
13126
      url_pattern_list<regex_provider>::create(std::move(texts), ignore_case);
13127
  if (!list) {
13128
    return list;
13129
  }
13130
  // Routes outside the subset reuse the pattern's already compiled pathname
13131
  // component: no second create_instance through the provider.
13132
  const url_pattern_list_detail::route_record* routes =
13133
      list->compiled_.template section<url_pattern_list_detail::route_record>(
13134
          list->compiled_.routes_offset);
13135
  for (size_t i = 0; i < patterns.size(); i++) {
13136
    if (routes[i].regexp_component < 0) {
13137
      continue;
13138
    }
13139
    list->compiled_.group_names[i] =
13140
        patterns[i].pathname_component.group_name_list;
13141
    list->regexp_components_.push_back(patterns[i].pathname_component);
13142
  }
13143
  return list;
13144
}
13145
#endif  // ADA_INCLUDE_URL_PATTERN
13146
13147
}  // namespace ada
13148
13149
#endif  // ADA_IMPLEMENTATION_INL_H
13150
/* end file include/ada/implementation-inl.h */
13151
13152
#endif  // ADA_H
13153
/* end file include/ada.h */