Coverage Report

Created: 2026-11-12 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ada-url/build/singleheader/ada.h
Line
Count
Source
1
/* auto-generated on 2026-11-07 22:18:56 -0500. Do not edit! */
2
/* begin file include/ada.h */
3
/**
4
 * @file ada.h
5
 * @brief Main header for the Ada URL parser library.
6
 *
7
 * This is the primary entry point for the Ada URL parser library. Including
8
 * this single header provides access to the complete Ada API, including:
9
 *
10
 * - URL parsing via `ada::parse()` function
11
 * - Two URL representations: `ada::url` and `ada::url_aggregator`
12
 * - URL search parameters via `ada::url_search_params`
13
 * - URL pattern matching via `ada::url_pattern` (URLPattern API)
14
 * - IDNA (Internationalized Domain Names) support
15
 *
16
 * @example
17
 * ```cpp
18
 *
19
 * // Parse a URL
20
 * auto url = ada::parse("https://example.com/path?query=1");
21
 * if (url) {
22
 *     std::cout << url->get_hostname(); // "example.com"
23
 * }
24
 * ```
25
 *
26
 * @see https://url.spec.whatwg.org/ - WHATWG URL Standard
27
 * @see https://github.com/ada-url/ada - Ada URL Parser GitHub Repository
28
 */
29
#ifndef ADA_H
30
#define ADA_H
31
32
/* begin file include/ada/ada_idna.h */
33
/* auto-generated on 2026-10-09 11:29:39 -0400. Do not edit! */
34
/* begin file include/idna.h */
35
#ifndef ADA_IDNA_H
36
#define ADA_IDNA_H
37
38
/* begin file include/ada/idna/unicode_transcoding.h */
39
#ifndef ADA_IDNA_UNICODE_TRANSCODING_H
40
#define ADA_IDNA_UNICODE_TRANSCODING_H
41
42
#include <string>
43
#include <string_view>
44
45
namespace ada::idna {
46
47
size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output);
48
49
size_t utf8_length_from_utf32(const char32_t* buf, size_t len);
50
51
size_t utf32_length_from_utf8(const char* buf, size_t len);
52
53
size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output);
54
55
}  // namespace ada::idna
56
57
#endif  // ADA_IDNA_UNICODE_TRANSCODING_H
58
/* end file include/ada/idna/unicode_transcoding.h */
59
/* begin file include/ada/idna/mapping.h */
60
#ifndef ADA_IDNA_MAPPING_H
61
#define ADA_IDNA_MAPPING_H
62
63
#include <string>
64
#include <string_view>
65
66
namespace ada::idna {
67
68
// If the input is ascii, then the mapping is just -> lower case.
69
void ascii_map(char* input, size_t length);
70
// Map the characters according to IDNA, returning the empty string on error.
71
std::u32string map(std::u32string_view input);
72
// Map into an existing buffer (cleared on entry). Returns false if any code
73
// point is disallowed. Reusing the buffer avoids repeated heap allocations
74
// when called in a loop over multiple labels.
75
bool map(std::u32string_view input, std::u32string& out);
76
77
}  // namespace ada::idna
78
79
#endif
80
/* end file include/ada/idna/mapping.h */
81
/* begin file include/ada/idna/normalization.h */
82
#ifndef ADA_IDNA_NORMALIZATION_H
83
#define ADA_IDNA_NORMALIZATION_H
84
85
#include <string>
86
#include <string_view>
87
88
namespace ada::idna {
89
90
// Returns true if `input` is already in Unicode Normalization Form C.
91
// Requires that internal tables have been loaded (call ensure via normalize
92
// or map first, or this returns false if tables are unavailable).
93
[[nodiscard]] bool is_already_nfc(std::u32string_view input) noexcept;
94
95
// Normalize the characters according to IDNA (Unicode Normalization Form C).
96
// Returns false if the internal Unicode tables could not be loaded; in that
97
// case `input` is left unchanged. Skips work when the string is already NFC.
98
[[nodiscard]] bool normalize(std::u32string& input);
99
100
}  // namespace ada::idna
101
#endif
102
/* end file include/ada/idna/normalization.h */
103
/* begin file include/ada/idna/punycode.h */
104
#ifndef ADA_IDNA_PUNYCODE_H
105
#define ADA_IDNA_PUNYCODE_H
106
107
#include <string>
108
#include <string_view>
109
110
namespace ada::idna {
111
112
bool punycode_to_utf32(std::string_view input, std::u32string& out);
113
bool verify_punycode(std::string_view input);
114
bool utf32_to_punycode(std::u32string_view input, std::string& out);
115
116
}  // namespace ada::idna
117
118
#endif  // ADA_IDNA_PUNYCODE_H
119
/* end file include/ada/idna/punycode.h */
120
/* begin file include/ada/idna/validity.h */
121
#ifndef ADA_IDNA_VALIDITY_H
122
#define ADA_IDNA_VALIDITY_H
123
124
#include <string>
125
#include <string_view>
126
127
namespace ada::idna {
128
129
/**
130
 * @see https://www.unicode.org/reports/tr46/#Validity_Criteria
131
 */
132
bool is_label_valid(std::u32string_view label);
133
134
}  // namespace ada::idna
135
136
#endif  // ADA_IDNA_VALIDITY_H
137
/* end file include/ada/idna/validity.h */
138
/* begin file include/ada/idna/to_ascii.h */
139
#ifndef ADA_IDNA_TO_ASCII_H
140
#define ADA_IDNA_TO_ASCII_H
141
142
#include <string>
143
#include <string_view>
144
145
/* begin file include/ada/idna/limits.h */
146
#ifndef ADA_IDNA_LIMITS_H
147
#define ADA_IDNA_LIMITS_H
148
149
#include <cstddef>
150
151
namespace ada::idna {
152
153
// Maximum accepted UTF-8 domain length for non-ASCII to_ascii input and for
154
// to_unicode input.
155
// Bounds heap growth under untrusted input (DoS resistance). DNS wire limits
156
// are smaller; this allows long Unicode labels used in URL tests/fixtures.
157
inline constexpr size_t max_domain_input_bytes = 16384;
158
159
}  // namespace ada::idna
160
161
#endif  // ADA_IDNA_LIMITS_H
162
/* end file include/ada/idna/limits.h */
163
164
namespace ada::idna {
165
166
// Converts a domain (e.g., www.google.com) possibly containing international
167
// characters to an ascii domain (with punycode). It will not do percent
168
// decoding: percent decoding should be done prior to calling this function. We
169
// do not remove tabs and spaces, they should have been removed prior to calling
170
// this function. We also do not trim control characters. We also assume that
171
// the input is not empty. We return "" on error. Non-ASCII inputs longer than
172
// max_domain_input_bytes are rejected; ASCII inputs of any length are accepted
173
// (they are only lowercased).
174
//
175
// This function may accept or even produce invalid domains (WHATWG carve-outs).
176
std::string to_ascii(std::string_view ut8_string);
177
178
// Same as to_ascii, but writes into `out` and returns false on error without
179
// relying on empty-string ambiguity.
180
[[nodiscard]] bool to_ascii(std::string_view ut8_string, std::string& out);
181
182
// Returns true if the string contains a forbidden code point according to the
183
// WHATGL URL specification:
184
// https://url.spec.whatwg.org/#forbidden-domain-code-point
185
bool contains_forbidden_domain_code_point(std::string_view ascii_string);
186
187
bool constexpr is_ascii(std::u32string_view view);
188
bool constexpr is_ascii(std::string_view view);
189
190
}  // namespace ada::idna
191
192
#endif  // ADA_IDNA_TO_ASCII_H
193
/* end file include/ada/idna/to_ascii.h */
194
/* begin file include/ada/idna/to_unicode.h */
195
#ifndef ADA_IDNA_TO_UNICODE_H
196
#define ADA_IDNA_TO_UNICODE_H
197
198
#include <string>
199
#include <string_view>
200
201
namespace ada::idna {
202
203
// UTS #46 ToUnicode. Never fails per the standard: on step failure the original
204
// label is kept. Inputs longer than max_domain_input_bytes are returned
205
// unchanged as a safety measure under untrusted input.
206
std::string to_unicode(std::string_view input);
207
208
// Writes into `out`. Returns false only if the input exceeds
209
// max_domain_input_bytes (out is left empty). Otherwise always returns true
210
// (ToUnicode does not fail).
211
[[nodiscard]] bool to_unicode(std::string_view input, std::string& out);
212
213
}  // namespace ada::idna
214
215
#endif  // ADA_IDNA_TO_UNICODE_H
216
/* end file include/ada/idna/to_unicode.h */
217
/* begin file include/ada/idna/identifier.h */
218
#ifndef ADA_IDNA_IDENTIFIER_H
219
#define ADA_IDNA_IDENTIFIER_H
220
221
#include <string>
222
#include <string_view>
223
224
namespace ada::idna {
225
226
// Verify if it is valid name code point given a Unicode code point and a
227
// boolean first: If first is true return the result of checking if code point
228
// is contained in the IdentifierStart set of code points. Otherwise return the
229
// result of checking if code point is contained in the IdentifierPart set of
230
// code points. Returns false if the input is empty or the code point is not
231
// valid. There is minimal Unicode error handling: the input should be valid
232
// UTF-8. https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point
233
bool valid_name_code_point(char32_t code_point, bool first);
234
235
}  // namespace ada::idna
236
237
#endif
238
/* end file include/ada/idna/identifier.h */
239
240
#endif
241
/* end file include/idna.h */
242
/* end file include/ada/ada_idna.h */
243
/* begin file include/ada/character_sets.h */
244
/**
245
 * @file character_sets.h
246
 * @brief Declaration of the character sets used by unicode functions.
247
 * @author Node.js
248
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
249
 */
250
#ifndef ADA_CHARACTER_SETS_H
251
#define ADA_CHARACTER_SETS_H
252
253
/* begin file include/ada/common_defs.h */
254
/**
255
 * @file common_defs.h
256
 * @brief Cross-platform compiler macros and common definitions.
257
 *
258
 * This header provides compiler-specific macros for optimization hints,
259
 * platform detection, SIMD support detection, and development/debug utilities.
260
 * It ensures consistent behavior across different compilers (GCC, Clang, MSVC).
261
 */
262
#ifndef ADA_COMMON_DEFS_H
263
#define ADA_COMMON_DEFS_H
264
265
// https://en.cppreference.com/w/cpp/feature_test#Library_features
266
// detect C++20 features
267
#include <version>
268
269
#ifdef _MSC_VER
270
#define ADA_VISUAL_STUDIO 1
271
/**
272
 * We want to differentiate carefully between
273
 * clang under visual studio and regular visual
274
 * studio.
275
 */
276
#ifdef __clang__
277
// clang under visual studio
278
#define ADA_CLANG_VISUAL_STUDIO 1
279
#else
280
// just regular visual studio (best guess)
281
#define ADA_REGULAR_VISUAL_STUDIO 1
282
#endif  // __clang__
283
#endif  // _MSC_VER
284
285
#if defined(__GNUC__)
286
// Marks a block with a name so that MCA analysis can see it.
287
#define ADA_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
288
#define ADA_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
289
#define ADA_DEBUG_BLOCK(name, block) \
290
  BEGIN_DEBUG_BLOCK(name);           \
291
  block;                             \
292
  END_DEBUG_BLOCK(name);
293
#else
294
#define ADA_BEGIN_DEBUG_BLOCK(name)
295
#define ADA_END_DEBUG_BLOCK(name)
296
#define ADA_DEBUG_BLOCK(name, block)
297
#endif
298
299
// Align to N-byte boundary
300
#define ADA_ROUNDUP_N(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
301
#define ADA_ROUNDDOWN_N(a, n) ((a) & ~((n) - 1))
302
303
#define ADA_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n) - 1)) == 0)
304
305
#if defined(ADA_REGULAR_VISUAL_STUDIO)
306
307
#define ada_really_inline __forceinline
308
#define ada_never_inline __declspec(noinline)
309
310
#define ada_unused
311
#define ada_warn_unused
312
313
#define ADA_PUSH_DISABLE_WARNINGS __pragma(warning(push))
314
#define ADA_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0))
315
#define ADA_DISABLE_VS_WARNING(WARNING_NUMBER) \
316
  __pragma(warning(disable : WARNING_NUMBER))
317
// Get rid of Intellisense-only warnings (Code Analysis)
318
// Though __has_include is C++17, it is supported in Visual Studio 2017 or
319
// better (_MSC_VER>=1910).
320
#ifdef __has_include
321
#if __has_include(<CppCoreCheck\Warnings.h>)
322
#include <CppCoreCheck\Warnings.h>
323
#define ADA_DISABLE_UNDESIRED_WARNINGS \
324
  ADA_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
325
#endif
326
#endif
327
328
#ifndef ADA_DISABLE_UNDESIRED_WARNINGS
329
#define ADA_DISABLE_UNDESIRED_WARNINGS
330
#endif
331
332
#define ADA_DISABLE_DEPRECATED_WARNING ADA_DISABLE_VS_WARNING(4996)
333
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING
334
#define ADA_POP_DISABLE_WARNINGS __pragma(warning(pop))
335
336
#else  // ADA_REGULAR_VISUAL_STUDIO
337
338
#define ada_really_inline inline __attribute__((always_inline))
339
#define ada_never_inline inline __attribute__((noinline))
340
341
#define ada_unused __attribute__((unused))
342
#define ada_warn_unused __attribute__((warn_unused_result))
343
344
#define ADA_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
345
// gcc doesn't seem to disable all warnings with all and extra, add warnings
346
// here as necessary
347
#define ADA_PUSH_DISABLE_ALL_WARNINGS               \
348
  ADA_PUSH_DISABLE_WARNINGS                         \
349
  ADA_DISABLE_GCC_WARNING("-Weffc++")               \
350
  ADA_DISABLE_GCC_WARNING("-Wall")                  \
351
  ADA_DISABLE_GCC_WARNING("-Wconversion")           \
352
  ADA_DISABLE_GCC_WARNING("-Wextra")                \
353
  ADA_DISABLE_GCC_WARNING("-Wattributes")           \
354
  ADA_DISABLE_GCC_WARNING("-Wimplicit-fallthrough") \
355
  ADA_DISABLE_GCC_WARNING("-Wnon-virtual-dtor")     \
356
  ADA_DISABLE_GCC_WARNING("-Wreturn-type")          \
357
  ADA_DISABLE_GCC_WARNING("-Wshadow")               \
358
  ADA_DISABLE_GCC_WARNING("-Wunused-parameter")     \
359
  ADA_DISABLE_GCC_WARNING("-Wunused-variable")      \
360
  ADA_DISABLE_GCC_WARNING("-Wsign-compare")
361
#define ADA_PRAGMA(P) _Pragma(#P)
362
#define ADA_DISABLE_GCC_WARNING(WARNING) \
363
  ADA_PRAGMA(GCC diagnostic ignored WARNING)
364
#if defined(ADA_CLANG_VISUAL_STUDIO)
365
#define ADA_DISABLE_UNDESIRED_WARNINGS \
366
  ADA_DISABLE_GCC_WARNING("-Wmicrosoft-include")
367
#else
368
#define ADA_DISABLE_UNDESIRED_WARNINGS
369
#endif
370
#define ADA_DISABLE_DEPRECATED_WARNING \
371
  ADA_DISABLE_GCC_WARNING("-Wdeprecated-declarations")
372
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING \
373
  ADA_DISABLE_GCC_WARNING("-Wstrict-overflow")
374
#define ADA_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
375
376
#endif  // MSC_VER
377
378
#if defined(ADA_VISUAL_STUDIO)
379
/**
380
 * It does not matter here whether you are using
381
 * the regular visual studio or clang under visual
382
 * studio.
383
 */
384
#if ADA_USING_LIBRARY
385
#define ADA_DLLIMPORTEXPORT __declspec(dllimport)
386
#else
387
#define ADA_DLLIMPORTEXPORT __declspec(dllexport)
388
#endif
389
#else
390
#define ADA_DLLIMPORTEXPORT
391
#endif
392
393
/// If EXPR is an error, returns it.
394
#define ADA_TRY(EXPR)   \
395
  {                     \
396
    auto _err = (EXPR); \
397
    if (_err) {         \
398
      return _err;      \
399
    }                   \
400
  }
401
402
// __has_cpp_attribute is part of C++20
403
#if !defined(__has_cpp_attribute)
404
#define __has_cpp_attribute(x) 0
405
#endif
406
407
#if __has_cpp_attribute(gnu::noinline)
408
#define ADA_ATTRIBUTE_NOINLINE [[gnu::noinline]]
409
#else
410
#define ADA_ATTRIBUTE_NOINLINE
411
#endif
412
413
namespace ada {
414
0
[[noreturn]] inline void unreachable() {
415
0
#ifdef __GNUC__
416
0
  __builtin_unreachable();
417
#elif defined(_MSC_VER)
418
  __assume(false);
419
#else
420
#endif
421
0
}
422
}  // namespace ada
423
424
// Unless the programmer has already set ADA_DEVELOPMENT_CHECKS,
425
// we want to set it under debug builds. We detect a debug build
426
// under Visual Studio when the _DEBUG macro is set. Under the other
427
// compilers, we use the fact that they define __OPTIMIZE__ whenever
428
// they allow optimizations.
429
// It is possible that this could miss some cases where ADA_DEVELOPMENT_CHECKS
430
// is helpful, but the programmer can set the macro ADA_DEVELOPMENT_CHECKS.
431
// It could also wrongly set ADA_DEVELOPMENT_CHECKS (e.g., if the programmer
432
// sets _DEBUG in a release build under Visual Studio, or if some compiler fails
433
// to set the __OPTIMIZE__ macro).
434
#if !defined(ADA_DEVELOPMENT_CHECKS) && !defined(NDEBUG)
435
#ifdef _MSC_VER
436
// Visual Studio seems to set _DEBUG for debug builds.
437
#ifdef _DEBUG
438
#define ADA_DEVELOPMENT_CHECKS 1
439
#endif  // _DEBUG
440
#else   // _MSC_VER
441
// All other compilers appear to set __OPTIMIZE__ to a positive integer
442
// when the compiler is optimizing.
443
#ifndef __OPTIMIZE__
444
#define ADA_DEVELOPMENT_CHECKS 1
445
#endif  // __OPTIMIZE__
446
#endif  // _MSC_VER
447
#endif  // ADA_DEVELOPMENT_CHECKS
448
449
#define ADA_STR(x) #x
450
451
#if ADA_DEVELOPMENT_CHECKS
452
#define ADA_REQUIRE(EXPR) \
453
  {                       \
454
    if (!(EXPR) { abort(); }) }
455
456
#define ADA_FAIL(MESSAGE)                            \
457
  do {                                               \
458
    std::cerr << "FAIL: " << (MESSAGE) << std::endl; \
459
    abort();                                         \
460
  } while (0);
461
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)                                    \
462
  do {                                                                         \
463
    if (LHS != RHS) {                                                          \
464
      std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \
465
      ADA_FAIL(MESSAGE);                                                       \
466
    }                                                                          \
467
  } while (0);
468
#define ADA_ASSERT_TRUE(COND)                                               \
469
  do {                                                                      \
470
    if (!(COND)) {                                                          \
471
      std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \
472
                << std::endl;                                               \
473
      ADA_FAIL(ADA_STR(COND));                                              \
474
    }                                                                       \
475
  } while (0);
476
#else
477
#define ADA_FAIL(MESSAGE)
478
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)
479
#define ADA_ASSERT_TRUE(COND)
480
#endif
481
482
#ifdef ADA_VISUAL_STUDIO
483
#define ADA_ASSUME(COND) __assume(COND)
484
#else
485
#define ADA_ASSUME(COND)       \
486
  do {                         \
487
    if (!(COND)) {             \
488
      __builtin_unreachable(); \
489
    }                          \
490
  } while (0)
491
#endif
492
493
#if defined(__SSSE3__)
494
#define ADA_SSSE3 1
495
#endif
496
497
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
498
    (defined(_M_AMD64) || defined(_M_X64) ||                         \
499
     (defined(_M_IX86_FP) && _M_IX86_FP == 2))
500
#define ADA_SSE2 1
501
#endif
502
503
// AVX-512 byte/word ops + 128/256-bit vectors of AVX-512 instructions.
504
// Used for optional high-performance IP address parsing kernels.
505
#if defined(__AVX512BW__) && defined(__AVX512VL__)
506
#define ADA_AVX512 1
507
#endif
508
509
#if defined(__aarch64__) || defined(_M_ARM64)
510
#define ADA_NEON 1
511
#endif
512
513
#if defined(__loongarch_sx)
514
#define ADA_LSX 1
515
#endif
516
517
#if defined(__riscv_v) && __riscv_v_intrinsic >= 11000
518
// Support RVV intrinsics v0.11 and above
519
#define ADA_RVV 1
520
#endif
521
522
#ifndef __has_cpp_attribute
523
#define ada_lifetime_bound
524
#elif __has_cpp_attribute(msvc::lifetimebound)
525
#define ada_lifetime_bound [[msvc::lifetimebound]]
526
#elif __has_cpp_attribute(clang::lifetimebound)
527
#define ada_lifetime_bound [[clang::lifetimebound]]
528
#elif __has_cpp_attribute(lifetimebound)
529
#define ada_lifetime_bound [[lifetimebound]]
530
#else
531
#define ada_lifetime_bound
532
#endif
533
534
#ifdef __cpp_lib_format
535
#if __cpp_lib_format >= 202110L
536
#include <format>
537
#define ADA_HAS_FORMAT 1
538
#endif
539
#endif
540
541
#ifndef ADA_INCLUDE_URL_PATTERN
542
#define ADA_INCLUDE_URL_PATTERN 1
543
#endif  // ADA_INCLUDE_URL_PATTERN
544
545
// When set (the default), NEON all-zero tests use an integer compare. Define
546
// it to 0 to reinterpret the bits as a double and compare against 0.0: that
547
// avoids a GPR transfer but is incorrect if flush-to-zero is enabled (e.g.,
548
// when the application is built with -ffast-math).
549
#ifndef ADA_NEON_SAFE_ZERO_CHECK
550
#define ADA_NEON_SAFE_ZERO_CHECK 1
551
#endif  // ADA_NEON_SAFE_ZERO_CHECK
552
553
#endif  // ADA_COMMON_DEFS_H
554
/* end file include/ada/common_defs.h */
555
#include <cstdint>
556
557
/**
558
 * These functions are not part of our public API and may
559
 * change at any time.
560
 * @private
561
 * @namespace ada::character_sets
562
 * @brief Includes the definitions for unicode character sets.
563
 */
564
namespace ada::character_sets {
565
ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i);
566
}  // namespace ada::character_sets
567
568
#endif  // ADA_CHARACTER_SETS_H
569
/* end file include/ada/character_sets.h */
570
/* begin file include/ada/character_sets-inl.h */
571
/**
572
 * @file character_sets-inl.h
573
 * @brief Definitions of the character sets used by unicode functions.
574
 * @author Node.js
575
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
576
 */
577
#ifndef ADA_CHARACTER_SETS_INL_H
578
#define ADA_CHARACTER_SETS_INL_H
579
580
581
#include <cstdint>
582
583
/**
584
 * These functions are not part of our public API and may
585
 * change at any time.
586
 * @private
587
 */
588
namespace ada::character_sets {
589
590
constexpr char hex[1024] =
591
    "%00\0%01\0%02\0%03\0%04\0%05\0%06\0%07\0"
592
    "%08\0%09\0%0A\0%0B\0%0C\0%0D\0%0E\0%0F\0"
593
    "%10\0%11\0%12\0%13\0%14\0%15\0%16\0%17\0"
594
    "%18\0%19\0%1A\0%1B\0%1C\0%1D\0%1E\0%1F\0"
595
    "%20\0%21\0%22\0%23\0%24\0%25\0%26\0%27\0"
596
    "%28\0%29\0%2A\0%2B\0%2C\0%2D\0%2E\0%2F\0"
597
    "%30\0%31\0%32\0%33\0%34\0%35\0%36\0%37\0"
598
    "%38\0%39\0%3A\0%3B\0%3C\0%3D\0%3E\0%3F\0"
599
    "%40\0%41\0%42\0%43\0%44\0%45\0%46\0%47\0"
600
    "%48\0%49\0%4A\0%4B\0%4C\0%4D\0%4E\0%4F\0"
601
    "%50\0%51\0%52\0%53\0%54\0%55\0%56\0%57\0"
602
    "%58\0%59\0%5A\0%5B\0%5C\0%5D\0%5E\0%5F\0"
603
    "%60\0%61\0%62\0%63\0%64\0%65\0%66\0%67\0"
604
    "%68\0%69\0%6A\0%6B\0%6C\0%6D\0%6E\0%6F\0"
605
    "%70\0%71\0%72\0%73\0%74\0%75\0%76\0%77\0"
606
    "%78\0%79\0%7A\0%7B\0%7C\0%7D\0%7E\0%7F\0"
607
    "%80\0%81\0%82\0%83\0%84\0%85\0%86\0%87\0"
608
    "%88\0%89\0%8A\0%8B\0%8C\0%8D\0%8E\0%8F\0"
609
    "%90\0%91\0%92\0%93\0%94\0%95\0%96\0%97\0"
610
    "%98\0%99\0%9A\0%9B\0%9C\0%9D\0%9E\0%9F\0"
611
    "%A0\0%A1\0%A2\0%A3\0%A4\0%A5\0%A6\0%A7\0"
612
    "%A8\0%A9\0%AA\0%AB\0%AC\0%AD\0%AE\0%AF\0"
613
    "%B0\0%B1\0%B2\0%B3\0%B4\0%B5\0%B6\0%B7\0"
614
    "%B8\0%B9\0%BA\0%BB\0%BC\0%BD\0%BE\0%BF\0"
615
    "%C0\0%C1\0%C2\0%C3\0%C4\0%C5\0%C6\0%C7\0"
616
    "%C8\0%C9\0%CA\0%CB\0%CC\0%CD\0%CE\0%CF\0"
617
    "%D0\0%D1\0%D2\0%D3\0%D4\0%D5\0%D6\0%D7\0"
618
    "%D8\0%D9\0%DA\0%DB\0%DC\0%DD\0%DE\0%DF\0"
619
    "%E0\0%E1\0%E2\0%E3\0%E4\0%E5\0%E6\0%E7\0"
620
    "%E8\0%E9\0%EA\0%EB\0%EC\0%ED\0%EE\0%EF\0"
621
    "%F0\0%F1\0%F2\0%F3\0%F4\0%F5\0%F6\0%F7\0"
622
    "%F8\0%F9\0%FA\0%FB\0%FC\0%FD\0%FE\0%FF";
623
624
constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32] = {
625
    // 00     01     02     03     04     05     06     07
626
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
627
    // 08     09     0A     0B     0C     0D     0E     0F
628
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
629
    // 10     11     12     13     14     15     16     17
630
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
631
    // 18     19     1A     1B     1C     1D     1E     1F
632
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
633
    // 20     21     22     23     24     25     26     27
634
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
635
    // 28     29     2A     2B     2C     2D     2E     2F
636
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
637
    // 30     31     32     33     34     35     36     37
638
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
639
    // 38     39     3A     3B     3C     3D     3E     3F
640
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
641
    // 40     41     42     43     44     45     46     47
642
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
643
    // 48     49     4A     4B     4C     4D     4E     4F
644
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
645
    // 50     51     52     53     54     55     56     57
646
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
647
    // 58     59     5A     5B     5C     5D     5E     5F
648
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
649
    // 60     61     62     63     64     65     66     67
650
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
651
    // 68     69     6A     6B     6C     6D     6E     6F
652
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
653
    // 70     71     72     73     74     75     76     77
654
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
655
    // 78     79     7A     7B     7C     7D     7E     7F
656
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
657
    // 80     81     82     83     84     85     86     87
658
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
659
    // 88     89     8A     8B     8C     8D     8E     8F
660
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
661
    // 90     91     92     93     94     95     96     97
662
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
663
    // 98     99     9A     9B     9C     9D     9E     9F
664
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
665
    // A0     A1     A2     A3     A4     A5     A6     A7
666
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
667
    // A8     A9     AA     AB     AC     AD     AE     AF
668
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
669
    // B0     B1     B2     B3     B4     B5     B6     B7
670
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
671
    // B8     B9     BA     BB     BC     BD     BE     BF
672
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
673
    // C0     C1     C2     C3     C4     C5     C6     C7
674
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
675
    // C8     C9     CA     CB     CC     CD     CE     CF
676
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
677
    // D0     D1     D2     D3     D4     D5     D6     D7
678
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
679
    // D8     D9     DA     DB     DC     DD     DE     DF
680
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
681
    // E0     E1     E2     E3     E4     E5     E6     E7
682
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
683
    // E8     E9     EA     EB     EC     ED     EE     EF
684
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
685
    // F0     F1     F2     F3     F4     F5     F6     F7
686
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
687
    // F8     F9     FA     FB     FC     FD     FE     FF
688
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
689
690
constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32] = {
691
    // 00     01     02     03     04     05     06     07
692
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
693
    // 08     09     0A     0B     0C     0D     0E     0F
694
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
695
    // 10     11     12     13     14     15     16     17
696
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
697
    // 18     19     1A     1B     1C     1D     1E     1F
698
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
699
    // 20     21     22     23     24     25     26     27
700
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x80,
701
    // 28     29     2A     2B     2C     2D     2E     2F
702
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
703
    // 30     31     32     33     34     35     36     37
704
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
705
    // 38     39     3A     3B     3C     3D     3E     3F
706
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
707
    // 40     41     42     43     44     45     46     47
708
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
709
    // 48     49     4A     4B     4C     4D     4E     4F
710
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
711
    // 50     51     52     53     54     55     56     57
712
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
713
    // 58     59     5A     5B     5C     5D     5E     5F
714
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
715
    // 60     61     62     63     64     65     66     67
716
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
717
    // 68     69     6A     6B     6C     6D     6E     6F
718
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
719
    // 70     71     72     73     74     75     76     77
720
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
721
    // 78     79     7A     7B     7C     7D     7E     7F
722
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
723
    // 80     81     82     83     84     85     86     87
724
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
725
    // 88     89     8A     8B     8C     8D     8E     8F
726
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
727
    // 90     91     92     93     94     95     96     97
728
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
729
    // 98     99     9A     9B     9C     9D     9E     9F
730
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
731
    // A0     A1     A2     A3     A4     A5     A6     A7
732
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
733
    // A8     A9     AA     AB     AC     AD     AE     AF
734
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
735
    // B0     B1     B2     B3     B4     B5     B6     B7
736
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
737
    // B8     B9     BA     BB     BC     BD     BE     BF
738
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
739
    // C0     C1     C2     C3     C4     C5     C6     C7
740
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
741
    // C8     C9     CA     CB     CC     CD     CE     CF
742
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
743
    // D0     D1     D2     D3     D4     D5     D6     D7
744
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
745
    // D8     D9     DA     DB     DC     DD     DE     DF
746
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
747
    // E0     E1     E2     E3     E4     E5     E6     E7
748
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
749
    // E8     E9     EA     EB     EC     ED     EE     EF
750
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
751
    // F0     F1     F2     F3     F4     F5     F6     F7
752
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
753
    // F8     F9     FA     FB     FC     FD     FE     FF
754
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
755
756
constexpr uint8_t QUERY_PERCENT_ENCODE[32] = {
757
    // 00     01     02     03     04     05     06     07
758
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
759
    // 08     09     0A     0B     0C     0D     0E     0F
760
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
761
    // 10     11     12     13     14     15     16     17
762
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
763
    // 18     19     1A     1B     1C     1D     1E     1F
764
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
765
    // 20     21     22     23     24     25     26     27
766
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
767
    // 28     29     2A     2B     2C     2D     2E     2F
768
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
769
    // 30     31     32     33     34     35     36     37
770
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
771
    // 38     39     3A     3B     3C     3D     3E     3F
772
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
773
    // 40     41     42     43     44     45     46     47
774
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
775
    // 48     49     4A     4B     4C     4D     4E     4F
776
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
777
    // 50     51     52     53     54     55     56     57
778
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
779
    // 58     59     5A     5B     5C     5D     5E     5F
780
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
781
    // 60     61     62     63     64     65     66     67
782
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
783
    // 68     69     6A     6B     6C     6D     6E     6F
784
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
785
    // 70     71     72     73     74     75     76     77
786
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
787
    // 78     79     7A     7B     7C     7D     7E     7F
788
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
789
    // 80     81     82     83     84     85     86     87
790
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
791
    // 88     89     8A     8B     8C     8D     8E     8F
792
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
793
    // 90     91     92     93     94     95     96     97
794
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
795
    // 98     99     9A     9B     9C     9D     9E     9F
796
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
797
    // A0     A1     A2     A3     A4     A5     A6     A7
798
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
799
    // A8     A9     AA     AB     AC     AD     AE     AF
800
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
801
    // B0     B1     B2     B3     B4     B5     B6     B7
802
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
803
    // B8     B9     BA     BB     BC     BD     BE     BF
804
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
805
    // C0     C1     C2     C3     C4     C5     C6     C7
806
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
807
    // C8     C9     CA     CB     CC     CD     CE     CF
808
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
809
    // D0     D1     D2     D3     D4     D5     D6     D7
810
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
811
    // D8     D9     DA     DB     DC     DD     DE     DF
812
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
813
    // E0     E1     E2     E3     E4     E5     E6     E7
814
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
815
    // E8     E9     EA     EB     EC     ED     EE     EF
816
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
817
    // F0     F1     F2     F3     F4     F5     F6     F7
818
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
819
    // F8     F9     FA     FB     FC     FD     FE     FF
820
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
821
822
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32] = {
823
    // 00     01     02     03     04     05     06     07
824
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
825
    // 08     09     0A     0B     0C     0D     0E     0F
826
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
827
    // 10     11     12     13     14     15     16     17
828
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
829
    // 18     19     1A     1B     1C     1D     1E     1F
830
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
831
    // 20     21     22     23     24     25     26     27
832
    0x01 | 0x00 | 0x04 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
833
    // 28     29     2A     2B     2C     2D     2E     2F
834
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
835
    // 30     31     32     33     34     35     36     37
836
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
837
    // 38     39     3A     3B     3C     3D     3E     3F
838
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
839
    // 40     41     42     43     44     45     46     47
840
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
841
    // 48     49     4A     4B     4C     4D     4E     4F
842
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
843
    // 50     51     52     53     54     55     56     57
844
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
845
    // 58     59     5A     5B     5C     5D     5E     5F
846
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
847
    // 60     61     62     63     64     65     66     67
848
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
849
    // 68     69     6A     6B     6C     6D     6E     6F
850
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
851
    // 70     71     72     73     74     75     76     77
852
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
853
    // 78     79     7A     7B     7C     7D     7E     7F
854
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
855
    // 80     81     82     83     84     85     86     87
856
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
857
    // 88     89     8A     8B     8C     8D     8E     8F
858
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
859
    // 90     91     92     93     94     95     96     97
860
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
861
    // 98     99     9A     9B     9C     9D     9E     9F
862
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
863
    // A0     A1     A2     A3     A4     A5     A6     A7
864
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
865
    // A8     A9     AA     AB     AC     AD     AE     AF
866
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
867
    // B0     B1     B2     B3     B4     B5     B6     B7
868
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
869
    // B8     B9     BA     BB     BC     BD     BE     BF
870
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
871
    // C0     C1     C2     C3     C4     C5     C6     C7
872
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
873
    // C8     C9     CA     CB     CC     CD     CE     CF
874
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
875
    // D0     D1     D2     D3     D4     D5     D6     D7
876
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
877
    // D8     D9     DA     DB     DC     DD     DE     DF
878
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
879
    // E0     E1     E2     E3     E4     E5     E6     E7
880
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
881
    // E8     E9     EA     EB     EC     ED     EE     EF
882
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
883
    // F0     F1     F2     F3     F4     F5     F6     F7
884
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
885
    // F8     F9     FA     FB     FC     FD     FE     FF
886
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
887
888
constexpr uint8_t USERINFO_PERCENT_ENCODE[32] = {
889
    // 00     01     02     03     04     05     06     07
890
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
891
    // 08     09     0A     0B     0C     0D     0E     0F
892
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
893
    // 10     11     12     13     14     15     16     17
894
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
895
    // 18     19     1A     1B     1C     1D     1E     1F
896
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
897
    // 20     21     22     23     24     25     26     27
898
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
899
    // 28     29     2A     2B     2C     2D     2E     2F
900
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
901
    // 30     31     32     33     34     35     36     37
902
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
903
    // 38     39     3A     3B     3C     3D     3E     3F
904
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
905
    // 40     41     42     43     44     45     46     47
906
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
907
    // 48     49     4A     4B     4C     4D     4E     4F
908
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
909
    // 50     51     52     53     54     55     56     57
910
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
911
    // 58     59     5A     5B     5C     5D     5E     5F
912
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
913
    // 60     61     62     63     64     65     66     67
914
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
915
    // 68     69     6A     6B     6C     6D     6E     6F
916
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
917
    // 70     71     72     73     74     75     76     77
918
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
919
    // 78     79     7A     7B     7C     7D     7E     7F
920
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80,
921
    // 80     81     82     83     84     85     86     87
922
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
923
    // 88     89     8A     8B     8C     8D     8E     8F
924
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
925
    // 90     91     92     93     94     95     96     97
926
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
927
    // 98     99     9A     9B     9C     9D     9E     9F
928
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
929
    // A0     A1     A2     A3     A4     A5     A6     A7
930
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
931
    // A8     A9     AA     AB     AC     AD     AE     AF
932
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
933
    // B0     B1     B2     B3     B4     B5     B6     B7
934
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
935
    // B8     B9     BA     BB     BC     BD     BE     BF
936
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
937
    // C0     C1     C2     C3     C4     C5     C6     C7
938
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
939
    // C8     C9     CA     CB     CC     CD     CE     CF
940
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
941
    // D0     D1     D2     D3     D4     D5     D6     D7
942
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
943
    // D8     D9     DA     DB     DC     DD     DE     DF
944
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
945
    // E0     E1     E2     E3     E4     E5     E6     E7
946
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
947
    // E8     E9     EA     EB     EC     ED     EE     EF
948
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
949
    // F0     F1     F2     F3     F4     F5     F6     F7
950
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
951
    // F8     F9     FA     FB     FC     FD     FE     FF
952
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
953
954
constexpr uint8_t PATH_PERCENT_ENCODE[32] = {
955
    // 00     01     02     03     04     05     06     07
956
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
957
    // 08     09     0A     0B     0C     0D     0E     0F
958
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
959
    // 10     11     12     13     14     15     16     17
960
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
961
    // 18     19     1A     1B     1C     1D     1E     1F
962
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
963
    // 20     21     22     23     24     25     26     27
964
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
965
    // 28     29     2A     2B     2C     2D     2E     2F
966
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
967
    // 30     31     32     33     34     35     36     37
968
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
969
    // 38     39     3A     3B     3C     3D     3E     3F
970
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
971
    // 40     41     42     43     44     45     46     47
972
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
973
    // 48     49     4A     4B     4C     4D     4E     4F
974
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
975
    // 50     51     52     53     54     55     56     57
976
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
977
    // 58     59     5A     5B     5C     5D     5E     5F
978
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x40 | 0x00,
979
    // 60     61     62     63     64     65     66     67
980
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
981
    // 68     69     6A     6B     6C     6D     6E     6F
982
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
983
    // 70     71     72     73     74     75     76     77
984
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
985
    // 78     79     7A     7B     7C     7D     7E     7F
986
    0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80,
987
    // 80     81     82     83     84     85     86     87
988
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
989
    // 88     89     8A     8B     8C     8D     8E     8F
990
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
991
    // 90     91     92     93     94     95     96     97
992
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
993
    // 98     99     9A     9B     9C     9D     9E     9F
994
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
995
    // A0     A1     A2     A3     A4     A5     A6     A7
996
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
997
    // A8     A9     AA     AB     AC     AD     AE     AF
998
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
999
    // B0     B1     B2     B3     B4     B5     B6     B7
1000
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1001
    // B8     B9     BA     BB     BC     BD     BE     BF
1002
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1003
    // C0     C1     C2     C3     C4     C5     C6     C7
1004
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1005
    // C8     C9     CA     CB     CC     CD     CE     CF
1006
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1007
    // D0     D1     D2     D3     D4     D5     D6     D7
1008
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1009
    // D8     D9     DA     DB     DC     DD     DE     DF
1010
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1011
    // E0     E1     E2     E3     E4     E5     E6     E7
1012
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1013
    // E8     E9     EA     EB     EC     ED     EE     EF
1014
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1015
    // F0     F1     F2     F3     F4     F5     F6     F7
1016
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1017
    // F8     F9     FA     FB     FC     FD     FE     FF
1018
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1019
1020
constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = {
1021
    // 00     01     02     03     04     05     06     07
1022
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1023
    // 08     09     0A     0B     0C     0D     0E     0F
1024
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1025
    // 10     11     12     13     14     15     16     17
1026
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1027
    // 18     19     1A     1B     1C     1D     1E     1F
1028
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1029
    // 20     21     22     23     24     25     26     27
1030
    0x00 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1031
    // 28     29     2A     2B     2C     2D     2E     2F
1032
    0x01 | 0x02 | 0x00 | 0x08 | 0x10 | 0x00 | 0x00 | 0x80,
1033
    // 30     31     32     33     34     35     36     37
1034
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1035
    // 38     39     3A     3B     3C     3D     3E     3F
1036
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1037
    // 40     41     42     43     44     45     46     47
1038
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1039
    // 48     49     4A     4B     4C     4D     4E     4F
1040
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1041
    // 50     51     52     53     54     55     56     57
1042
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1043
    // 58     59     5A     5B     5C     5D     5E     5F
1044
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
1045
    // 60     61     62     63     64     65     66     67
1046
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1047
    // 68     69     6A     6B     6C     6D     6E     6F
1048
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1049
    // 70     71     72     73     74     75     76     77
1050
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1051
    // 78     79     7A     7B     7C     7D     7E     7F
1052
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1053
    // 80     81     82     83     84     85     86     87
1054
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1055
    // 88     89     8A     8B     8C     8D     8E     8F
1056
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1057
    // 90     91     92     93     94     95     96     97
1058
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1059
    // 98     99     9A     9B     9C     9D     9E     9F
1060
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1061
    // A0     A1     A2     A3     A4     A5     A6     A7
1062
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1063
    // A8     A9     AA     AB     AC     AD     AE     AF
1064
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1065
    // B0     B1     B2     B3     B4     B5     B6     B7
1066
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1067
    // B8     B9     BA     BB     BC     BD     BE     BF
1068
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1069
    // C0     C1     C2     C3     C4     C5     C6     C7
1070
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1071
    // C8     C9     CA     CB     CC     CD     CE     CF
1072
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1073
    // D0     D1     D2     D3     D4     D5     D6     D7
1074
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1075
    // D8     D9     DA     DB     DC     DD     DE     DF
1076
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1077
    // E0     E1     E2     E3     E4     E5     E6     E7
1078
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1079
    // E8     E9     EA     EB     EC     ED     EE     EF
1080
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1081
    // F0     F1     F2     F3     F4     F5     F6     F7
1082
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1083
    // F8     F9     FA     FB     FC     FD     FE     FF
1084
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1085
1086
8.64M
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1087
8.64M
  return !!(a[i >> 3] & (1 << (i & 7)));
1088
8.64M
}
1089
1090
}  // namespace ada::character_sets
1091
1092
#endif  // ADA_CHARACTER_SETS_INL_H
1093
/* end file include/ada/character_sets-inl.h */
1094
/* begin file include/ada/checkers-inl.h */
1095
/**
1096
 * @file checkers-inl.h
1097
 * @brief Definitions for URL specific checkers used within Ada.
1098
 */
1099
#ifndef ADA_CHECKERS_INL_H
1100
#define ADA_CHECKERS_INL_H
1101
1102
#include <bit>
1103
#include <cstddef>
1104
#include <cstdint>
1105
#include <string_view>
1106
/* begin file include/ada/checkers.h */
1107
/**
1108
 * @file checkers.h
1109
 * @brief Declarations for URL specific checkers used within Ada.
1110
 */
1111
#ifndef ADA_CHECKERS_H
1112
#define ADA_CHECKERS_H
1113
1114
1115
#include <cstdint>
1116
#include <cstring>
1117
#include <string_view>
1118
1119
/**
1120
 * These functions are not part of our public API and may
1121
 * change at any time.
1122
 * @private
1123
 * @namespace ada::checkers
1124
 * @brief Includes the definitions for validation functions
1125
 */
1126
namespace ada::checkers {
1127
1128
/**
1129
 * @private
1130
 * Assuming that x is an ASCII letter, this function returns the lower case
1131
 * equivalent.
1132
 * @details More likely to be inlined by the compiler and constexpr.
1133
 */
1134
constexpr char to_lower(char x) noexcept;
1135
1136
/**
1137
 * @private
1138
 * Returns true if the character is an ASCII letter. Equivalent to std::isalpha
1139
 * but more likely to be inlined by the compiler.
1140
 *
1141
 * @attention std::isalpha is not constexpr generally.
1142
 */
1143
constexpr bool is_alpha(char x) noexcept;
1144
1145
/**
1146
 * @private
1147
 * Check whether x is an ASCII digit. More likely to be inlined than
1148
 * std::isdigit.
1149
 */
1150
constexpr bool is_digit(char x) noexcept;
1151
1152
/**
1153
 * @private
1154
 * @details A string starts with a Windows drive letter if all of the following
1155
 * are true:
1156
 *
1157
 *   - its length is greater than or equal to 2
1158
 *   - its first two code points are a Windows drive letter
1159
 *   - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F
1160
 * (?), or U+0023 (#).
1161
 *
1162
 * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
1163
 */
1164
inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept;
1165
1166
/**
1167
 * @private
1168
 * @details A normalized Windows drive letter is a Windows drive letter of which
1169
 * the second code point is U+003A (:).
1170
 */
1171
inline constexpr bool is_normalized_windows_drive_letter(
1172
    std::string_view input) noexcept;
1173
1174
/**
1175
 * @private
1176
 * Returns true if an input is an ipv4 address. It is assumed that the string
1177
 * does not contain uppercase ASCII characters (the input should have been
1178
 * lowered cased before calling this function) and is not empty.
1179
 */
1180
ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept;
1181
1182
/**
1183
 * @private
1184
 * Cheap pre-check for the WHATWG "ends in a number" / IPv4-host cases.
1185
 * False means the host cannot be IPv4: the last label is empty, does not
1186
 * start with an ASCII digit, or contains a character other than a hex
1187
 * digit or x/X. True means the slower IPv4 parsers should run.
1188
 *
1189
 * Safe on mixed-case input (unlike is_ipv4, which expects a lowercased
1190
 * host).
1191
 */
1192
ada_really_inline constexpr bool last_label_may_be_a_number(
1193
    std::string_view view) noexcept;
1194
1195
/**
1196
 * @private
1197
 * Returns a bitset. If the first bit is set, then at least one character needs
1198
 * percent encoding. If the second bit is set, a \\ is found. If the third bit
1199
 * is set then we have a dot. If the fourth bit is set, then we have a percent
1200
 * character.
1201
 */
1202
ada_really_inline constexpr uint8_t path_signature(
1203
    std::string_view input) noexcept;
1204
1205
/**
1206
 * @private
1207
 * Returns true if the length of the domain name and its labels are according to
1208
 * the specifications. The length of the domain must be 255 octets (253
1209
 * characters not including the last 2 which are the empty label reserved at the
1210
 * end). When the empty label is included (a dot at the end), the domain name
1211
 * can have 254 characters. The length of a label must be at least 1 and at most
1212
 * 63 characters.
1213
 * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034
1214
 * @see https://www.unicode.org/reports/tr46/#ToASCII
1215
 */
1216
ada_really_inline constexpr bool verify_dns_length(
1217
    std::string_view input) noexcept;
1218
1219
/**
1220
 * @private
1221
 * Fast-path parser for pure decimal IPv4 addresses (e.g., "192.168.1.1").
1222
 * Returns the packed 32-bit IPv4 address on success, or a value > 0xFFFFFFFF
1223
 * to indicate failure (caller should fall back to general parser).
1224
 * This is optimized for the common case where the input is a well-formed
1225
 * decimal IPv4 address with exactly 4 octets.
1226
 */
1227
ada_really_inline uint64_t try_parse_ipv4_fast(std::string_view input) noexcept;
1228
1229
/**
1230
 * Sentinel value indicating try_parse_ipv4_fast() did not succeed.
1231
 * Any value > 0xFFFFFFFF indicates the fast path should not be used.
1232
 */
1233
constexpr uint64_t ipv4_fast_fail = uint64_t(1) << 32;
1234
1235
}  // namespace ada::checkers
1236
1237
#endif  // ADA_CHECKERS_H
1238
/* end file include/ada/checkers.h */
1239
1240
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1241
#include <immintrin.h>
1242
#endif
1243
1244
namespace ada::checkers {
1245
1246
95.5k
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1247
1248
468k
constexpr bool is_ipv4_number_char(char x) noexcept {
1249
468k
  const unsigned char c = static_cast<unsigned char>(x);
1250
468k
  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
1251
265k
         (c >= 'A' && c <= 'F') || c == 'x' || c == 'X';
1252
468k
}
1253
1254
245k
constexpr bool last_label_may_be_a_number(std::string_view view) noexcept {
1255
245k
  if (view.empty()) {
1256
0
    return false;
1257
0
  }
1258
245k
  const char* start = view.data();
1259
245k
  const char* end = start + view.size();
1260
245k
  if (end[-1] == '.') {
1261
8.68k
    --end;
1262
8.68k
    if (end == start) {
1263
4.07k
      return false;
1264
4.07k
    }
1265
8.68k
  }
1266
241k
  if (!is_ipv4_number_char(end[-1])) {
1267
197k
    return false;
1268
197k
  }
1269
43.8k
  const char* label = end;
1270
249k
  while (label != start && is_ipv4_number_char(label[-1])) {
1271
205k
    --label;
1272
205k
  }
1273
43.8k
  if (label != start && label[-1] != '.') {
1274
12.7k
    return false;
1275
12.7k
  }
1276
31.0k
  return label != end && is_digit(*label);
1277
43.8k
}
1278
1279
402k
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1280
1281
226k
constexpr bool is_alpha(char x) noexcept {
1282
226k
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1283
226k
}
1284
1285
38.6k
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1286
38.6k
  return input.size() >= 2 &&
1287
34.2k
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1288
4.25k
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1289
672
                                  input[2] == '?' || input[2] == '#'));
1290
38.6k
}
1291
1292
constexpr bool is_normalized_windows_drive_letter(
1293
11.6k
    std::string_view input) noexcept {
1294
11.6k
  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
1295
11.6k
}
1296
1297
namespace detail {
1298
1299
// Unrolled pure-decimal IPv4. The common portable path for 7-16 byte hosts.
1300
ada_really_inline uint64_t
1301
65.7k
parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
1302
65.7k
  uint32_t ipv4 = 0;
1303
83.4k
  for (int i = 0; i < 4; ++i) {
1304
79.9k
    if (p == pend) [[unlikely]] {
1305
38
      return ipv4_fast_fail;
1306
38
    }
1307
79.9k
    uint32_t val;
1308
79.9k
    char c = *p;
1309
79.9k
    if (c >= '0' && c <= '9') [[likely]] {
1310
25.5k
      val = static_cast<uint32_t>(c - '0');
1311
25.5k
      ++p;
1312
54.3k
    } else {
1313
54.3k
      return ipv4_fast_fail;
1314
54.3k
    }
1315
25.5k
    if (p < pend) {
1316
23.8k
      c = *p;
1317
23.8k
      if (c >= '0' && c <= '9') {
1318
9.64k
        if (val == 0) [[unlikely]] {
1319
1.29k
          return ipv4_fast_fail;
1320
1.29k
        }
1321
8.35k
        val = val * 10u + static_cast<uint32_t>(c - '0');
1322
8.35k
        ++p;
1323
8.35k
        if (p < pend) {
1324
7.53k
          c = *p;
1325
7.53k
          if (c >= '0' && c <= '9') {
1326
4.80k
            val = val * 10u + static_cast<uint32_t>(c - '0');
1327
4.80k
            ++p;
1328
4.80k
            if (val > 255u) [[unlikely]] {
1329
1.35k
              return ipv4_fast_fail;
1330
1.35k
            }
1331
4.80k
          }
1332
7.53k
        }
1333
8.35k
      }
1334
23.8k
    }
1335
22.8k
    ipv4 = (ipv4 << 8) | val;
1336
22.8k
    if (i < 3) {
1337
19.4k
      if (p == pend || *p != '.') [[unlikely]] {
1338
5.21k
        return ipv4_fast_fail;
1339
5.21k
      }
1340
14.1k
      ++p;
1341
14.1k
    }
1342
22.8k
  }
1343
3.47k
  if (p != pend) {
1344
1.64k
    if (p == pend - 1 && *p == '.') {
1345
301
      return ipv4;
1346
301
    }
1347
1.34k
    return ipv4_fast_fail;
1348
1.64k
  }
1349
1.83k
  return ipv4;
1350
3.47k
}
1351
1352
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1353
// Table-free AVX-512VL IPv4 parse (simdip parse_ipv4_avx512vl_notab5).
1354
// Needs VBMI2 for vpcompressb; ADA_AVX512 stays BW+VL (IPv6 does not use
1355
// VBMI2). Masked load of exactly `len` bytes (no over-read). Digit
1356
// placement is computed from compressed delimiter positions; octet > 255
1357
// is a dword compare on the zero-padded reversed digit group, in parallel
1358
// with the convert. Unusual-but-valid forms (octal, hex, leading zeros,
1359
// fewer than four parts) return ipv4_fast_fail so the general parser runs.
1360
ada_really_inline uint64_t try_parse_ipv4_avx512(const char* data,
1361
                                                 size_t len) noexcept {
1362
  // One trailing dot is WHATWG-legal ("1.2.3.4."); the SIMD kernel is
1363
  // strict four-group dotted-decimal.
1364
  if (data[len - 1] == '.') {
1365
    --len;
1366
  }
1367
  if (len > 15) [[unlikely]] {
1368
    return ipv4_fast_fail;
1369
  }
1370
1371
#if defined(__BMI2__)
1372
  const uint32_t len_mask = _bzhi_u32(0xFFFFFFFFu, static_cast<unsigned>(len));
1373
#else
1374
  const uint32_t len_mask = (1u << static_cast<unsigned>(len)) - 1u;
1375
#endif
1376
  const __mmask16 len_k = static_cast<__mmask16>(len_mask);
1377
  const __m128i dot = _mm_set1_epi8('.');
1378
  const __m128i v =
1379
      _mm_mask_loadu_epi8(dot, len_k, reinterpret_cast<const void*>(data));
1380
1381
  const __mmask16 delim = _mm_cmpeq_epi8_mask(v, dot);
1382
  const uint32_t dots = static_cast<uint32_t>(delim) & len_mask;
1383
  const uint32_t keep = len_mask & ~dots;
1384
1385
  const __m128i zero = _mm_set1_epi8('0');
1386
  const __m128i digits = _mm_sub_epi8(v, zero);
1387
  const __mmask16 is_digit = _mm_cmple_epu8_mask(digits, _mm_set1_epi8(9));
1388
  // Junk in a digit slot: inside [0, len) yet neither a digit nor a dot.
1389
  const __mmask16 hole = _kandn_mask16(_kor_mask16(delim, is_digit), len_k);
1390
  const __m128i v0 = _mm_maskz_mov_epi8(is_digit, digits);
1391
1392
  const __m128i iota =
1393
      _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
1394
  const __m128i c = _mm_maskz_compress_epi8(delim, iota);
1395
  const __m128i k_rep =
1396
      _mm_setr_epi8(0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3);
1397
  const __m128i qi = _mm_shuffle_epi8(c, k_rep);
1398
  const __m128i prev =
1399
      _mm_shuffle_epi8(_mm_alignr_epi8(c, _mm_set1_epi8(-1), 15), k_rep);
1400
  // Reversed group order: lane 4i+j fetches q_i-(j+1), so the dword is
1401
  // ones | tens<<8 | hundreds<<16, monotone in the decimal value.
1402
  const __m128i offr = _mm_setr_epi8(-1, -2, -3, -4, -1, -2, -3, -4, -1, -2, -3,
1403
                                     -4, -1, -2, -3, -4);
1404
  const __m128i idx = _mm_max_epi8(_mm_add_epi8(qi, offr), prev);
1405
  const __m128i padded = _mm_shuffle_epi8(v0, idx);
1406
1407
  const __m128i lim =
1408
      _mm_setr_epi8(5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0);
1409
  const __mmask8 over = _mm_cmpgt_epu32_mask(padded, lim);
1410
  const __m128i wtsr =
1411
      _mm_setr_epi8(1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0);
1412
#if defined(__AVX512VNNI__)
1413
  const __m128i res = _mm_dpbusd_epi32(_mm_setzero_si128(), padded, wtsr);
1414
#else
1415
  const __m128i res =
1416
      _mm_madd_epi16(_mm_maddubs_epi16(padded, wtsr), _mm_set1_epi16(1));
1417
#endif
1418
1419
  const __m128i gmin =
1420
      _mm_setr_epi8(1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1421
  const __m128i gmax =
1422
      _mm_setr_epi8(2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
1423
  const __m128i gap = _mm_sub_epi8(c, _mm_slli_si128(c, 1));
1424
  const __mmask16 bad_gap = _mm_cmpgt_epu8_mask(_mm_sub_epi8(gap, gmin), gmax);
1425
1426
  const uint32_t zero_bits =
1427
      static_cast<uint32_t>(_mm_cmpeq_epi8_mask(v, zero));
1428
  const uint32_t start_bits = (dots << 1) | 1u;
1429
1430
  const __mmask16 kerr =
1431
      _kor_mask16(_kor_mask16(hole, static_cast<__mmask16>(over)), bad_gap);
1432
  const uint32_t gerr = static_cast<uint32_t>(_mm_popcnt_u32(dots) ^ 3u) |
1433
                        (zero_bits & start_bits & (keep >> 1));
1434
1435
  if ((gerr | static_cast<uint32_t>(kerr)) == 0) [[likely]] {
1436
    const uint32_t packed =
1437
        static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_cvtepi32_epi8(res)));
1438
#if defined(__GNUC__) || defined(__clang__)
1439
    return static_cast<uint64_t>(__builtin_bswap32(packed));
1440
#else
1441
    return static_cast<uint64_t>(_byteswap_ulong(packed));
1442
#endif
1443
  }
1444
  return ipv4_fast_fail;
1445
}
1446
#endif  // ADA_AVX512 && __AVX512VBMI2__
1447
1448
}  // namespace detail
1449
1450
/**
1451
 * Fast pure-decimal IPv4 parse. Returns packed address or ipv4_fast_fail.
1452
 * Accepts an optional single trailing dot.
1453
 *
1454
 * On AVX-512BW+VL+VBMI2 targets, uses a table-free SIMD parse (masked
1455
 * load, no source over-read) based on parse_ipv4_avx512vl_notab5.
1456
 * Otherwise uses an unrolled scalar path (typically faster than SSE2/NEON
1457
 * pre-validation for these 7-16 byte hosts).
1458
 */
1459
ada_really_inline uint64_t
1460
151k
try_parse_ipv4_fast(std::string_view input) noexcept {
1461
151k
  const size_t len = input.size();
1462
  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
1463
151k
  if (len < 7 || len > 16) [[unlikely]] {
1464
85.3k
    return ipv4_fast_fail;
1465
85.3k
  }
1466
65.7k
  const char* data = input.data();
1467
1468
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1469
  return detail::try_parse_ipv4_avx512(data, len);
1470
#else
1471
65.7k
  return detail::parse_ipv4_decimal_scalar(data, data + len);
1472
151k
#endif
1473
151k
}
1474
1475
}  // namespace ada::checkers
1476
1477
#endif  // ADA_CHECKERS_INL_H
1478
/* end file include/ada/checkers-inl.h */
1479
/* begin file include/ada/log.h */
1480
/**
1481
 * @file log.h
1482
 * @brief Includes the definitions for logging.
1483
 * @private Excluded from docs through the doxygen file.
1484
 */
1485
#ifndef ADA_LOG_H
1486
#define ADA_LOG_H
1487
1488
// To enable logging, set ADA_LOGGING to 1:
1489
#ifndef ADA_LOGGING
1490
#define ADA_LOGGING 0
1491
#endif
1492
1493
#if ADA_LOGGING
1494
#include <iostream>
1495
#endif  // ADA_LOGGING
1496
1497
namespace ada {
1498
1499
/**
1500
 * Log a message. If you want to have no overhead when logging is disabled, use
1501
 * the ada_log macro.
1502
 * @private
1503
 */
1504
template <typename... Args>
1505
constexpr ada_really_inline void log([[maybe_unused]] Args... args) {
1506
#if ADA_LOGGING
1507
  ((std::cout << "ADA_LOG: ") << ... << args) << std::endl;
1508
#endif  // ADA_LOGGING
1509
}
1510
}  // namespace ada
1511
1512
#if ADA_LOGGING
1513
#ifndef ada_log
1514
#define ada_log(...)       \
1515
  do {                     \
1516
    ada::log(__VA_ARGS__); \
1517
  } while (0)
1518
#endif  // ada_log
1519
#else
1520
#define ada_log(...)
1521
#endif  // ADA_LOGGING
1522
1523
#endif  // ADA_LOG_H
1524
/* end file include/ada/log.h */
1525
/* begin file include/ada/encoding_type.h */
1526
/**
1527
 * @file encoding_type.h
1528
 * @brief Character encoding type definitions.
1529
 *
1530
 * Defines the encoding types supported for URL processing.
1531
 *
1532
 * @see https://encoding.spec.whatwg.org/
1533
 */
1534
#ifndef ADA_ENCODING_TYPE_H
1535
#define ADA_ENCODING_TYPE_H
1536
1537
#include <string>
1538
#include <string_view>
1539
1540
namespace ada {
1541
1542
/**
1543
 * @brief Character encoding types for URL processing.
1544
 *
1545
 * Specifies the character encoding used for percent-decoding and other
1546
 * string operations. UTF-8 is the most commonly used encoding for URLs.
1547
 *
1548
 * @see https://encoding.spec.whatwg.org/#encodings
1549
 */
1550
enum class encoding_type {
1551
  UTF8,     /**< UTF-8 encoding (default for URLs) */
1552
  UTF_16LE, /**< UTF-16 Little Endian encoding */
1553
  UTF_16BE, /**< UTF-16 Big Endian encoding */
1554
};
1555
1556
/**
1557
 * Converts an encoding_type to its string representation.
1558
 * @param type The encoding type to convert.
1559
 * @return A string view of the encoding name.
1560
 */
1561
ada_warn_unused std::string_view to_string(encoding_type type);
1562
1563
}  // namespace ada
1564
1565
#endif  // ADA_ENCODING_TYPE_H
1566
/* end file include/ada/encoding_type.h */
1567
/* begin file include/ada/helpers.h */
1568
/**
1569
 * @file helpers.h
1570
 * @brief Definitions for helper functions used within Ada.
1571
 */
1572
#ifndef ADA_HELPERS_H
1573
#define ADA_HELPERS_H
1574
1575
/* begin file include/ada/url_base.h */
1576
/**
1577
 * @file url_base.h
1578
 * @brief Base class and common definitions for URL types.
1579
 *
1580
 * This file defines the `url_base` abstract base class from which both
1581
 * `ada::url` and `ada::url_aggregator` inherit. It also defines common
1582
 * enumerations like `url_host_type`.
1583
 */
1584
#ifndef ADA_URL_BASE_H
1585
#define ADA_URL_BASE_H
1586
1587
/* begin file include/ada/scheme.h */
1588
/**
1589
 * @file scheme.h
1590
 * @brief URL scheme type definitions and utilities.
1591
 *
1592
 * This header defines the URL scheme types (http, https, etc.) and provides
1593
 * functions to identify special schemes and their default ports according
1594
 * to the WHATWG URL Standard.
1595
 *
1596
 * @see https://url.spec.whatwg.org/#special-scheme
1597
 */
1598
#ifndef ADA_SCHEME_H
1599
#define ADA_SCHEME_H
1600
1601
1602
#include <cstdint>
1603
#include <string>
1604
#include <string_view>
1605
1606
/**
1607
 * @namespace ada::scheme
1608
 * @brief URL scheme utilities and constants.
1609
 *
1610
 * Provides functions for working with URL schemes, including identification
1611
 * of special schemes and retrieval of default port numbers.
1612
 */
1613
namespace ada::scheme {
1614
1615
/**
1616
 * @brief Enumeration of URL scheme types.
1617
 *
1618
 * Special schemes have specific parsing rules and default ports.
1619
 * Using an enum allows efficient scheme comparisons without string operations.
1620
 *
1621
 * Default ports:
1622
 * - HTTP: 80
1623
 * - HTTPS: 443
1624
 * - WS: 80
1625
 * - WSS: 443
1626
 * - FTP: 21
1627
 * - FILE: (none)
1628
 */
1629
enum type : uint8_t {
1630
  HTTP = 0,        /**< http:// scheme (port 80) */
1631
  NOT_SPECIAL = 1, /**< Non-special scheme (no default port) */
1632
  HTTPS = 2,       /**< https:// scheme (port 443) */
1633
  WS = 3,          /**< ws:// WebSocket scheme (port 80) */
1634
  FTP = 4,         /**< ftp:// scheme (port 21) */
1635
  WSS = 5,         /**< wss:// secure WebSocket scheme (port 443) */
1636
  FILE = 6         /**< file:// scheme (no default port) */
1637
};
1638
1639
/**
1640
 * Checks if a scheme string is a special scheme.
1641
 * @param scheme The scheme string to check (e.g., "http", "https").
1642
 * @return `true` if the scheme is special, `false` otherwise.
1643
 * @see https://url.spec.whatwg.org/#special-scheme
1644
 */
1645
ada_really_inline constexpr bool is_special(std::string_view scheme);
1646
1647
/**
1648
 * Returns the default port for a special scheme string.
1649
 * @param scheme The scheme string (e.g., "http", "https").
1650
 * @return The default port number, or 0 if not a special scheme.
1651
 * @see https://url.spec.whatwg.org/#special-scheme
1652
 */
1653
constexpr uint16_t get_special_port(std::string_view scheme) noexcept;
1654
1655
/**
1656
 * Returns the default port for a scheme type.
1657
 * @param type The scheme type enum value.
1658
 * @return The default port number, or 0 if not applicable.
1659
 * @see https://url.spec.whatwg.org/#special-scheme
1660
 */
1661
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept;
1662
1663
/**
1664
 * Converts a scheme string to its type enum.
1665
 * @param scheme The scheme string to convert.
1666
 * @return The corresponding scheme type, or NOT_SPECIAL if not recognized.
1667
 */
1668
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept;
1669
1670
}  // namespace ada::scheme
1671
1672
#endif  // ADA_SCHEME_H
1673
/* end file include/ada/scheme.h */
1674
1675
#include <cstddef>
1676
#include <cstdint>
1677
#include <string>
1678
#include <string_view>
1679
1680
namespace ada {
1681
1682
/**
1683
 * @brief Enum representing the type of host in a URL.
1684
 *
1685
 * Used to distinguish between regular domain names, IPv4 addresses,
1686
 * and IPv6 addresses for proper parsing and serialization.
1687
 */
1688
enum url_host_type : uint8_t {
1689
  /** Regular domain name (e.g., "www.example.com") */
1690
  DEFAULT = 0,
1691
  /** IPv4 address (e.g., "127.0.0.1") */
1692
  IPV4 = 1,
1693
  /** IPv6 address (e.g., "[::1]" or "[2001:db8::1]") */
1694
  IPV6 = 2,
1695
};
1696
1697
/**
1698
 * @brief Abstract base class for URL representations.
1699
 *
1700
 * The `url_base` class provides the common interface and state shared by
1701
 * both `ada::url` and `ada::url_aggregator`. It contains basic URL attributes
1702
 * like validity status and scheme type, but delegates component storage and
1703
 * access to derived classes.
1704
 *
1705
 * @note This is an abstract class and cannot be instantiated directly.
1706
 *       Use `ada::url` or `ada::url_aggregator` instead.
1707
 *
1708
 * @see url
1709
 * @see url_aggregator
1710
 */
1711
struct url_base {
1712
681k
  virtual ~url_base() = default;
1713
1714
  /**
1715
   * Indicates whether the URL was successfully parsed.
1716
   * Set to `false` if parsing failed (e.g., invalid URL syntax).
1717
   */
1718
  bool is_valid{true};
1719
1720
  /**
1721
   * Indicates whether the URL has an opaque path (non-hierarchical).
1722
   * Opaque paths occur in non-special URLs like `mailto:` or `javascript:`.
1723
   */
1724
  bool has_opaque_path{false};
1725
1726
  /**
1727
   * The type of the URL's host (domain, IPv4, or IPv6).
1728
   */
1729
  url_host_type host_type = url_host_type::DEFAULT;
1730
1731
  /**
1732
   * @private
1733
   * Internal representation of the URL's scheme type.
1734
   */
1735
  ada::scheme::type type{ada::scheme::type::NOT_SPECIAL};
1736
1737
  /**
1738
   * Checks if the URL has a special scheme (http, https, ws, wss, ftp, file).
1739
   * Special schemes have specific parsing rules and default ports.
1740
   * @return `true` if the scheme is special, `false` otherwise.
1741
   */
1742
  [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept;
1743
1744
  /**
1745
   * Returns the URL's origin (scheme + host + port for special URLs).
1746
   * @return A newly allocated string containing the serialized origin.
1747
   * @see https://url.spec.whatwg.org/#concept-url-origin
1748
   */
1749
  [[nodiscard]] virtual std::string get_origin() const = 0;
1750
1751
  /**
1752
   * Validates whether the hostname is a valid domain according to RFC 1034.
1753
   * Checks that the domain and its labels have valid lengths.
1754
   * @return `true` if the domain is valid, `false` otherwise.
1755
   */
1756
  [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0;
1757
1758
  /**
1759
   * @private
1760
   * Returns the default port for special schemes (e.g., 443 for https).
1761
   * Returns 0 for file:// URLs or non-special schemes.
1762
   */
1763
  [[nodiscard]] inline uint16_t get_special_port() const noexcept;
1764
1765
  /**
1766
   * @private
1767
   * Returns the default port for the URL's scheme, or 0 if none.
1768
   */
1769
  [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept;
1770
1771
  /**
1772
   * @private
1773
   * Parses a port number from the input string.
1774
   * @param view The string containing the port to parse.
1775
   * @param check_trailing_content Whether to validate no trailing characters.
1776
   * @return Number of bytes consumed on success, 0 on failure.
1777
   */
1778
  virtual size_t parse_port(std::string_view view,
1779
                            bool check_trailing_content) = 0;
1780
1781
  /** @private */
1782
0
  virtual ada_really_inline size_t parse_port(std::string_view view) {
1783
0
    return this->parse_port(view, false);
1784
0
  }
1785
1786
  /**
1787
   * Returns a JSON string representation of this URL for debugging.
1788
   * @return A JSON-formatted string with URL information.
1789
   */
1790
  [[nodiscard]] virtual std::string to_string() const = 0;
1791
1792
  /** @private */
1793
  virtual inline void clear_pathname() = 0;
1794
1795
  /** @private */
1796
  virtual inline void clear_search() = 0;
1797
1798
  /** @private */
1799
  [[nodiscard]] virtual inline bool has_hash() const noexcept = 0;
1800
1801
  /** @private */
1802
  [[nodiscard]] virtual inline bool has_search() const noexcept = 0;
1803
1804
};  // url_base
1805
1806
}  // namespace ada
1807
1808
#endif
1809
/* end file include/ada/url_base.h */
1810
1811
#include <cstddef>
1812
#include <cstdint>
1813
#include <optional>
1814
#include <string>
1815
#include <string_view>
1816
#include <utility>
1817
1818
#if ADA_DEVELOPMENT_CHECKS
1819
#include <cstdlib>
1820
#include <iostream>
1821
#endif  // ADA_DEVELOPMENT_CHECKS
1822
1823
/**
1824
 * These functions are not part of our public API and may
1825
 * change at any time.
1826
 *
1827
 * @private
1828
 * @namespace ada::helpers
1829
 * @brief Includes the definitions for helper functions
1830
 */
1831
namespace ada::helpers {
1832
1833
/**
1834
 * @private
1835
 */
1836
template <typename out_iter>
1837
void encode_json(std::string_view view, out_iter out);
1838
1839
/**
1840
 * @private
1841
 * This function is used to prune a fragment from a url, and returning the
1842
 * removed string if input has fragment.
1843
 *
1844
 * @details prune_hash seeks the first '#' and returns everything after it
1845
 * as a string_view, and modifies (in place) the input so that it points at
1846
 * everything before the '#'. If no '#' is found, the input is left unchanged
1847
 * and std::nullopt is returned.
1848
 *
1849
 * @attention The function is non-allocating and it does not throw.
1850
 * @returns Note that the returned string_view might be empty!
1851
 */
1852
ada_really_inline std::optional<std::string_view> prune_hash(
1853
    std::string_view& input) noexcept;
1854
1855
/**
1856
 * @private
1857
 * Defined by the URL specification, shorten a URLs paths.
1858
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1859
 * @returns Returns true if path is shortened.
1860
 */
1861
ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type);
1862
1863
/**
1864
 * @private
1865
 * Defined by the URL specification, shorten a URLs paths.
1866
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1867
 * @returns Returns true if path is shortened.
1868
 */
1869
ada_really_inline bool shorten_path(std::string_view& path,
1870
                                    ada::scheme::type type);
1871
1872
/**
1873
 * @private
1874
 *
1875
 * Parse the path from the provided input and append to the existing
1876
 * (possibly empty) path. The input cannot contain tabs and spaces: it
1877
 * is the user's responsibility to check.
1878
 *
1879
 * The input is expected to be UTF-8.
1880
 *
1881
 * @see https://url.spec.whatwg.org/
1882
 */
1883
ada_really_inline void parse_prepared_path(std::string_view input,
1884
                                           ada::scheme::type type,
1885
                                           std::string& path);
1886
1887
/**
1888
 * @private
1889
 * Remove and mutate all ASCII tab or newline characters from an input.
1890
 */
1891
ada_really_inline void remove_ascii_tab_or_newline(std::string& input);
1892
1893
/**
1894
 * @private
1895
 * Return the substring from input going from index pos to the end.
1896
 */
1897
ada_really_inline constexpr std::string_view substring(std::string_view input,
1898
                                                       size_t pos);
1899
1900
/**
1901
 * @private
1902
 * Returns true if the string_view points within the string.
1903
 */
1904
bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1905
1906
/**
1907
 * @private
1908
 * Return the substring from input going from index pos1 to the pos2 (non
1909
 * included). The length of the substring is pos2 - pos1.
1910
 */
1911
ada_really_inline constexpr std::string_view substring(std::string_view input,
1912
                                                       size_t pos1,
1913
755k
                                                       size_t pos2) {
1914
#if ADA_DEVELOPMENT_CHECKS
1915
  if (pos2 < pos1) {
1916
    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1917
              << std::endl;
1918
    abort();
1919
  }
1920
#endif
1921
755k
  return input.substr(pos1, pos2 - pos1);
1922
755k
}
1923
1924
/**
1925
 * @private
1926
 * Modify the string_view so that it has the new size pos, assuming that pos <=
1927
 * input.size(). This function cannot throw.
1928
 */
1929
ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1930
1931
/**
1932
 * @private
1933
 * Returns a host's delimiter location depending on the state of the instance,
1934
 * and whether a colon was found outside brackets. Used by the host parser.
1935
 */
1936
ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1937
    bool is_special, std::string_view& view) noexcept;
1938
1939
/**
1940
 * @private
1941
 * Removes leading and trailing C0 control and whitespace characters from
1942
 * string.
1943
 */
1944
void trim_c0_whitespace(std::string_view& input) noexcept;
1945
1946
/**
1947
 * @private
1948
 * @see
1949
 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1950
 */
1951
template <class url_type>
1952
ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url);
1953
1954
/**
1955
 * @private
1956
 * Finds the delimiter of a view in authority state.
1957
 */
1958
ada_really_inline size_t
1959
find_authority_delimiter_special(std::string_view view) noexcept;
1960
1961
/**
1962
 * @private
1963
 * Finds the delimiter of a view in authority state.
1964
 */
1965
ada_really_inline size_t
1966
find_authority_delimiter(std::string_view view) noexcept;
1967
1968
/**
1969
 * @private
1970
 */
1971
template <typename T, typename... Args>
1972
268k
inline void inner_concat(std::string& buffer, T t) {
1973
268k
  buffer.append(t);
1974
268k
}
void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1972
57.0k
inline void inner_concat(std::string& buffer, T t) {
1973
57.0k
  buffer.append(t);
1974
57.0k
}
void ada::helpers::inner_concat<char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*)
Line
Count
Source
1972
163k
inline void inner_concat(std::string& buffer, T t) {
1973
163k
  buffer.append(t);
1974
163k
}
void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
1972
48.1k
inline void inner_concat(std::string& buffer, T t) {
1973
48.1k
  buffer.append(t);
1974
48.1k
}
1975
1976
/**
1977
 * @private
1978
 */
1979
template <typename T, typename... Args>
1980
349k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1981
349k
  buffer.append(t);
1982
349k
  return inner_concat(buffer, args...);
1983
349k
}
void ada::helpers::inner_concat<char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1980
57.0k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1981
57.0k
  buffer.append(t);
1982
57.0k
  return inner_concat(buffer, args...);
1983
57.0k
}
void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1980
32.7k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1981
32.7k
  buffer.append(t);
1982
32.7k
  return inner_concat(buffer, args...);
1983
32.7k
}
void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Line
Count
Source
1980
148k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1981
148k
  buffer.append(t);
1982
148k
  return inner_concat(buffer, args...);
1983
148k
}
void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Line
Count
Source
1980
15.2k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1981
15.2k
  buffer.append(t);
1982
15.2k
  return inner_concat(buffer, args...);
1983
15.2k
}
void ada::helpers::inner_concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
1980
48.1k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1981
48.1k
  buffer.append(t);
1982
48.1k
  return inner_concat(buffer, args...);
1983
48.1k
}
void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
1980
48.1k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1981
48.1k
  buffer.append(t);
1982
48.1k
  return inner_concat(buffer, args...);
1983
48.1k
}
1984
1985
/**
1986
 * @private
1987
 * Concatenate the arguments and return a string.
1988
 * @returns a string
1989
 */
1990
template <typename... Args>
1991
268k
std::string concat(Args... args) {
1992
268k
  std::string answer;
1993
268k
  inner_concat(answer, args...);
1994
268k
  return answer;
1995
268k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1991
24.3k
std::string concat(Args... args) {
1992
24.3k
  std::string answer;
1993
24.3k
  inner_concat(answer, args...);
1994
24.3k
  return answer;
1995
24.3k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1991
32.7k
std::string concat(Args... args) {
1992
32.7k
  std::string answer;
1993
32.7k
  inner_concat(answer, args...);
1994
32.7k
  return answer;
1995
32.7k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Line
Count
Source
1991
148k
std::string concat(Args... args) {
1992
148k
  std::string answer;
1993
148k
  inner_concat(answer, args...);
1994
148k
  return answer;
1995
148k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Line
Count
Source
1991
15.2k
std::string concat(Args... args) {
1992
15.2k
  std::string answer;
1993
15.2k
  inner_concat(answer, args...);
1994
15.2k
  return answer;
1995
15.2k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
1991
26
std::string concat(Args... args) {
1992
26
  std::string answer;
1993
26
  inner_concat(answer, args...);
1994
26
  return answer;
1995
26
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
1991
48.1k
std::string concat(Args... args) {
1992
48.1k
  std::string answer;
1993
48.1k
  inner_concat(answer, args...);
1994
48.1k
  return answer;
1995
48.1k
}
1996
1997
/**
1998
 * @private
1999
 * @return Number of leading zeroes.
2000
 */
2001
155k
inline int leading_zeroes(uint32_t input_num) noexcept {
2002
#if ADA_REGULAR_VISUAL_STUDIO
2003
  unsigned long leading_zero(0);
2004
  unsigned long in(input_num);
2005
  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
2006
#else
2007
155k
  return __builtin_clz(input_num);
2008
155k
#endif  // ADA_REGULAR_VISUAL_STUDIO
2009
155k
}
2010
2011
/**
2012
 * @private
2013
 * Counts the number of decimal digits necessary to represent x.
2014
 * faster than std::to_string(x).size().
2015
 * @return digit count
2016
 */
2017
40.7k
inline int fast_digit_count(uint32_t x) noexcept {
2018
40.7k
  auto int_log2 = [](uint32_t z) -> int {
2019
40.7k
    return 31 - ada::helpers::leading_zeroes(z | 1);
2020
40.7k
  };
2021
  // Compiles to very few instructions. Note that the
2022
  // table is static and thus effectively a constant.
2023
  // We leave it inside the function because it is meaningless
2024
  // outside of it (this comes at no performance cost).
2025
40.7k
  const static uint64_t table[] = {
2026
40.7k
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
2027
40.7k
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
2028
40.7k
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
2029
40.7k
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
2030
40.7k
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
2031
40.7k
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
2032
40.7k
      42949672960, 42949672960};
2033
40.7k
  return int((x + table[int_log2(x)]) >> 32);
2034
40.7k
}
2035
}  // namespace ada::helpers
2036
2037
#endif  // ADA_HELPERS_H
2038
/* end file include/ada/helpers.h */
2039
/* begin file include/ada/parser.h */
2040
/**
2041
 * @file parser.h
2042
 * @brief Low-level URL parsing functions.
2043
 *
2044
 * This header provides the internal URL parsing implementation. Most users
2045
 * should use `ada::parse()` from implementation.h instead of these functions
2046
 * directly.
2047
 *
2048
 * @see implementation.h for the recommended public API
2049
 */
2050
#ifndef ADA_PARSER_H
2051
#define ADA_PARSER_H
2052
2053
#include <cstddef>
2054
#include <cstdint>
2055
#include <string_view>
2056
#include <variant>
2057
2058
/* begin file include/ada/expected.h */
2059
/**
2060
 * @file expected.h
2061
 * @brief Definitions for std::expected
2062
 * @private Excluded from docs through the doxygen file.
2063
 */
2064
///
2065
// expected - An implementation of std::expected with extensions
2066
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
2067
//
2068
// Documentation available at http://tl.tartanllama.xyz/
2069
//
2070
// To the extent possible under law, the author(s) have dedicated all
2071
// copyright and related and neighboring rights to this software to the
2072
// public domain worldwide. This software is distributed without any warranty.
2073
//
2074
// You should have received a copy of the CC0 Public Domain Dedication
2075
// along with this software. If not, see
2076
// <http://creativecommons.org/publicdomain/zero/1.0/>.
2077
///
2078
2079
#ifndef TL_EXPECTED_HPP
2080
#define TL_EXPECTED_HPP
2081
2082
#define TL_EXPECTED_VERSION_MAJOR 1
2083
#define TL_EXPECTED_VERSION_MINOR 1
2084
#define TL_EXPECTED_VERSION_PATCH 0
2085
2086
#include <cstddef>
2087
#include <exception>
2088
#include <functional>
2089
#include <initializer_list>
2090
#include <type_traits>
2091
#include <utility>
2092
2093
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
2094
#define TL_EXPECTED_EXCEPTIONS_ENABLED
2095
#endif
2096
2097
#if (defined(_MSC_VER) && _MSC_VER == 1900)
2098
#define TL_EXPECTED_MSVC2015
2099
#define TL_EXPECTED_MSVC2015_CONSTEXPR
2100
#else
2101
#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
2102
#endif
2103
2104
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2105
     !defined(__clang__))
2106
#define TL_EXPECTED_GCC49
2107
#endif
2108
2109
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
2110
     !defined(__clang__))
2111
#define TL_EXPECTED_GCC54
2112
#endif
2113
2114
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
2115
     !defined(__clang__))
2116
#define TL_EXPECTED_GCC55
2117
#endif
2118
2119
#if !defined(TL_ASSERT)
2120
// can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
2121
#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
2122
#include <cassert>
2123
2.34M
#define TL_ASSERT(x) assert(x)
2124
#else
2125
#define TL_ASSERT(x)
2126
#endif
2127
#endif
2128
2129
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2130
     !defined(__clang__))
2131
// GCC < 5 doesn't support overloading on const&& for member functions
2132
2133
#define TL_EXPECTED_NO_CONSTRR
2134
// GCC < 5 doesn't support some standard C++11 type traits
2135
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2136
  std::has_trivial_copy_constructor<T>
2137
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2138
  std::has_trivial_copy_assign<T>
2139
2140
// This one will be different for GCC 5.7 if it's ever supported
2141
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2142
  std::is_trivially_destructible<T>
2143
2144
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
2145
// std::vector for non-copyable types
2146
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
2147
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2148
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2149
namespace tl {
2150
namespace detail {
2151
template <class T>
2152
struct is_trivially_copy_constructible
2153
    : std::is_trivially_copy_constructible<T> {};
2154
#ifdef _GLIBCXX_VECTOR
2155
template <class T, class A>
2156
struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
2157
#endif
2158
}  // namespace detail
2159
}  // namespace tl
2160
#endif
2161
2162
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2163
  tl::detail::is_trivially_copy_constructible<T>
2164
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2165
  std::is_trivially_copy_assignable<T>
2166
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2167
  std::is_trivially_destructible<T>
2168
#else
2169
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2170
  std::is_trivially_copy_constructible<T>
2171
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2172
  std::is_trivially_copy_assignable<T>
2173
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2174
  std::is_trivially_destructible<T>
2175
#endif
2176
2177
#if __cplusplus > 201103L
2178
#define TL_EXPECTED_CXX14
2179
#endif
2180
2181
#ifdef TL_EXPECTED_GCC49
2182
#define TL_EXPECTED_GCC49_CONSTEXPR
2183
#else
2184
#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
2185
#endif
2186
2187
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
2188
     defined(TL_EXPECTED_GCC49))
2189
#define TL_EXPECTED_11_CONSTEXPR
2190
#else
2191
#define TL_EXPECTED_11_CONSTEXPR constexpr
2192
#endif
2193
2194
namespace tl {
2195
template <class T, class E>
2196
class expected;
2197
2198
#ifndef TL_MONOSTATE_INPLACE_MUTEX
2199
#define TL_MONOSTATE_INPLACE_MUTEX
2200
class monostate {};
2201
2202
struct in_place_t {
2203
  explicit in_place_t() = default;
2204
};
2205
static constexpr in_place_t in_place{};
2206
#endif
2207
2208
template <class E>
2209
class unexpected {
2210
 public:
2211
  static_assert(!std::is_same<E, void>::value, "E must not be void");
2212
2213
  unexpected() = delete;
2214
  constexpr explicit unexpected(const E& e) : m_val(e) {}
2215
2216
129k
  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
2217
2218
  template <class... Args, typename std::enable_if<std::is_constructible<
2219
                               E, Args&&...>::value>::type* = nullptr>
2220
  constexpr explicit unexpected(Args&&... args)
2221
0
      : m_val(std::forward<Args>(args)...) {}
2222
  template <
2223
      class U, class... Args,
2224
      typename std::enable_if<std::is_constructible<
2225
          E, std::initializer_list<U>&, Args&&...>::value>::type* = nullptr>
2226
  constexpr explicit unexpected(std::initializer_list<U> l, Args&&... args)
2227
      : m_val(l, std::forward<Args>(args)...) {}
2228
2229
  constexpr const E& value() const& { return m_val; }
2230
64.5k
  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
2231
  TL_EXPECTED_11_CONSTEXPR E&& value() && { return std::move(m_val); }
2232
  constexpr const E&& value() const&& { return std::move(m_val); }
2233
2234
 private:
2235
  E m_val;
2236
};
2237
2238
#ifdef __cpp_deduction_guides
2239
template <class E>
2240
unexpected(E) -> unexpected<E>;
2241
#endif
2242
2243
template <class E>
2244
constexpr bool operator==(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2245
  return lhs.value() == rhs.value();
2246
}
2247
template <class E>
2248
constexpr bool operator!=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2249
  return lhs.value() != rhs.value();
2250
}
2251
template <class E>
2252
constexpr bool operator<(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2253
  return lhs.value() < rhs.value();
2254
}
2255
template <class E>
2256
constexpr bool operator<=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2257
  return lhs.value() <= rhs.value();
2258
}
2259
template <class E>
2260
constexpr bool operator>(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2261
  return lhs.value() > rhs.value();
2262
}
2263
template <class E>
2264
constexpr bool operator>=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2265
  return lhs.value() >= rhs.value();
2266
}
2267
2268
template <class E>
2269
unexpected<typename std::decay<E>::type> make_unexpected(E&& e) {
2270
  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
2271
}
2272
2273
struct unexpect_t {
2274
  unexpect_t() = default;
2275
};
2276
static constexpr unexpect_t unexpect{};
2277
2278
namespace detail {
2279
template <typename E>
2280
0
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E&& e) {
2281
0
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2282
0
  throw std::forward<E>(e);
2283
#else
2284
  (void)e;
2285
#ifdef _MSC_VER
2286
  __assume(0);
2287
#else
2288
  __builtin_unreachable();
2289
#endif
2290
#endif
2291
0
}
2292
2293
#ifndef TL_TRAITS_MUTEX
2294
#define TL_TRAITS_MUTEX
2295
// C++14-style aliases for brevity
2296
template <class T>
2297
using remove_const_t = typename std::remove_const<T>::type;
2298
template <class T>
2299
using remove_reference_t = typename std::remove_reference<T>::type;
2300
template <class T>
2301
using decay_t = typename std::decay<T>::type;
2302
template <bool E, class T = void>
2303
using enable_if_t = typename std::enable_if<E, T>::type;
2304
template <bool B, class T, class F>
2305
using conditional_t = typename std::conditional<B, T, F>::type;
2306
2307
// std::conjunction from C++17
2308
template <class...>
2309
struct conjunction : std::true_type {};
2310
template <class B>
2311
struct conjunction<B> : B {};
2312
template <class B, class... Bs>
2313
struct conjunction<B, Bs...>
2314
    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2315
2316
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2317
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2318
#endif
2319
2320
// In C++11 mode, there's an issue in libc++'s std::mem_fn
2321
// which results in a hard-error when using it in a noexcept expression
2322
// in some cases. This is a check to workaround the common failing case.
2323
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2324
template <class T>
2325
struct is_pointer_to_non_const_member_func : std::false_type {};
2326
template <class T, class Ret, class... Args>
2327
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
2328
    : std::true_type {};
2329
template <class T, class Ret, class... Args>
2330
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
2331
    : std::true_type {};
2332
template <class T, class Ret, class... Args>
2333
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
2334
    : std::true_type {};
2335
template <class T, class Ret, class... Args>
2336
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
2337
    : std::true_type {};
2338
template <class T, class Ret, class... Args>
2339
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&>
2340
    : std::true_type {};
2341
template <class T, class Ret, class... Args>
2342
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&>
2343
    : std::true_type {};
2344
2345
template <class T>
2346
struct is_const_or_const_ref : std::false_type {};
2347
template <class T>
2348
struct is_const_or_const_ref<T const&> : std::true_type {};
2349
template <class T>
2350
struct is_const_or_const_ref<T const> : std::true_type {};
2351
#endif
2352
2353
// std::invoke from C++17
2354
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2355
template <
2356
    typename Fn, typename... Args,
2357
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2358
    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2359
                             is_const_or_const_ref<Args...>::value)>,
2360
#endif
2361
    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2362
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2363
    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2364
    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2365
  return std::mem_fn(f)(std::forward<Args>(args)...);
2366
}
2367
2368
template <typename Fn, typename... Args,
2369
          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2370
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2371
    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2372
    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2373
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
2374
}
2375
2376
// std::invoke_result from C++17
2377
template <class F, class, class... Us>
2378
struct invoke_result_impl;
2379
2380
template <class F, class... Us>
2381
struct invoke_result_impl<
2382
    F,
2383
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2384
    Us...> {
2385
  using type =
2386
      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2387
};
2388
2389
template <class F, class... Us>
2390
using invoke_result = invoke_result_impl<F, void, Us...>;
2391
2392
template <class F, class... Us>
2393
using invoke_result_t = typename invoke_result<F, Us...>::type;
2394
2395
#if defined(_MSC_VER) && _MSC_VER <= 1900
2396
// TODO make a version which works with MSVC 2015
2397
template <class T, class U = T>
2398
struct is_swappable : std::true_type {};
2399
2400
template <class T, class U = T>
2401
struct is_nothrow_swappable : std::true_type {};
2402
#else
2403
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2404
namespace swap_adl_tests {
2405
// if swap ADL finds this then it would call std::swap otherwise (same
2406
// signature)
2407
struct tag {};
2408
2409
template <class T>
2410
tag swap(T&, T&);
2411
template <class T, std::size_t N>
2412
tag swap(T (&a)[N], T (&b)[N]);
2413
2414
// helper functions to test if an unqualified swap is possible, and if it
2415
// becomes std::swap
2416
template <class, class>
2417
std::false_type can_swap(...) noexcept(false);
2418
template <class T, class U,
2419
          class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
2420
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(),
2421
                                                    std::declval<U&>())));
2422
2423
template <class, class>
2424
std::false_type uses_std(...);
2425
template <class T, class U>
2426
std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag>
2427
uses_std(int);
2428
2429
template <class T>
2430
struct is_std_swap_noexcept
2431
    : std::integral_constant<bool,
2432
                             std::is_nothrow_move_constructible<T>::value &&
2433
                                 std::is_nothrow_move_assignable<T>::value> {};
2434
2435
template <class T, std::size_t N>
2436
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2437
2438
template <class T, class U>
2439
struct is_adl_swap_noexcept
2440
    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2441
}  // namespace swap_adl_tests
2442
2443
template <class T, class U = T>
2444
struct is_swappable
2445
    : std::integral_constant<
2446
          bool,
2447
          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2448
              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2449
               (std::is_move_assignable<T>::value &&
2450
                std::is_move_constructible<T>::value))> {};
2451
2452
template <class T, std::size_t N>
2453
struct is_swappable<T[N], T[N]>
2454
    : std::integral_constant<
2455
          bool,
2456
          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2457
              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2458
                   0))::value ||
2459
               is_swappable<T, T>::value)> {};
2460
2461
template <class T, class U = T>
2462
struct is_nothrow_swappable
2463
    : std::integral_constant<
2464
          bool,
2465
          is_swappable<T, U>::value &&
2466
              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2467
                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2468
               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2469
                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2470
#endif
2471
#endif
2472
2473
// Trait for checking if a type is a tl::expected
2474
template <class T>
2475
struct is_expected_impl : std::false_type {};
2476
template <class T, class E>
2477
struct is_expected_impl<expected<T, E>> : std::true_type {};
2478
template <class T>
2479
using is_expected = is_expected_impl<decay_t<T>>;
2480
2481
template <class T, class E, class U>
2482
using expected_enable_forward_value = detail::enable_if_t<
2483
    std::is_constructible<T, U&&>::value &&
2484
    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2485
    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2486
    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2487
2488
template <class T, class E, class U, class G, class UR, class GR>
2489
using expected_enable_from_other = detail::enable_if_t<
2490
    std::is_constructible<T, UR>::value &&
2491
    std::is_constructible<E, GR>::value &&
2492
    !std::is_constructible<T, expected<U, G>&>::value &&
2493
    !std::is_constructible<T, expected<U, G>&&>::value &&
2494
    !std::is_constructible<T, const expected<U, G>&>::value &&
2495
    !std::is_constructible<T, const expected<U, G>&&>::value &&
2496
    !std::is_convertible<expected<U, G>&, T>::value &&
2497
    !std::is_convertible<expected<U, G>&&, T>::value &&
2498
    !std::is_convertible<const expected<U, G>&, T>::value &&
2499
    !std::is_convertible<const expected<U, G>&&, T>::value>;
2500
2501
template <class T, class U>
2502
using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2503
2504
template <class T>
2505
using is_copy_constructible_or_void =
2506
    is_void_or<T, std::is_copy_constructible<T>>;
2507
2508
template <class T>
2509
using is_move_constructible_or_void =
2510
    is_void_or<T, std::is_move_constructible<T>>;
2511
2512
template <class T>
2513
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2514
2515
template <class T>
2516
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2517
2518
}  // namespace detail
2519
2520
namespace detail {
2521
struct no_init_t {};
2522
static constexpr no_init_t no_init{};
2523
2524
// Implements the storage of the values, and ensures that the destructor is
2525
// trivial if it can be.
2526
//
2527
// This specialization is for where neither `T` or `E` is trivially
2528
// destructible, so the destructors must be called on destruction of the
2529
// `expected`
2530
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2531
          bool = std::is_trivially_destructible<E>::value>
2532
struct expected_storage_base {
2533
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2534
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2535
2536
  template <class... Args,
2537
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2538
                nullptr>
2539
  constexpr expected_storage_base(in_place_t, Args&&... args)
2540
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2541
2542
  template <class U, class... Args,
2543
            detail::enable_if_t<std::is_constructible<
2544
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2545
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2546
                                  Args&&... args)
2547
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2548
  template <class... Args,
2549
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2550
                nullptr>
2551
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2552
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2553
2554
  template <class U, class... Args,
2555
            detail::enable_if_t<std::is_constructible<
2556
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2557
  constexpr explicit expected_storage_base(unexpect_t,
2558
                                           std::initializer_list<U> il,
2559
                                           Args&&... args)
2560
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2561
2562
  ~expected_storage_base() {
2563
    if (m_has_val) {
2564
      m_val.~T();
2565
    } else {
2566
      m_unexpect.~unexpected<E>();
2567
    }
2568
  }
2569
  union {
2570
    T m_val;
2571
    unexpected<E> m_unexpect;
2572
    char m_no_init;
2573
  };
2574
  bool m_has_val;
2575
};
2576
2577
// This specialization is for when both `T` and `E` are trivially-destructible,
2578
// so the destructor of the `expected` can be trivial.
2579
template <class T, class E>
2580
struct expected_storage_base<T, E, true, true> {
2581
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2582
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2583
2584
  template <class... Args,
2585
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2586
                nullptr>
2587
  constexpr expected_storage_base(in_place_t, Args&&... args)
2588
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_
2589
2590
  template <class U, class... Args,
2591
            detail::enable_if_t<std::is_constructible<
2592
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2593
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2594
                                  Args&&... args)
2595
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2596
  template <class... Args,
2597
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2598
                nullptr>
2599
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2600
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2601
2602
  template <class U, class... Args,
2603
            detail::enable_if_t<std::is_constructible<
2604
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2605
  constexpr explicit expected_storage_base(unexpect_t,
2606
                                           std::initializer_list<U> il,
2607
                                           Args&&... args)
2608
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2609
2610
  ~expected_storage_base() = default;
2611
  union {
2612
    T m_val;
2613
    unexpected<E> m_unexpect;
2614
    char m_no_init;
2615
  };
2616
  bool m_has_val;
2617
};
2618
2619
// T is trivial, E is not.
2620
template <class T, class E>
2621
struct expected_storage_base<T, E, true, false> {
2622
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2623
  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2624
      : m_no_init(), m_has_val(false) {}
2625
2626
  template <class... Args,
2627
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2628
                nullptr>
2629
  constexpr expected_storage_base(in_place_t, Args&&... args)
2630
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2631
2632
  template <class U, class... Args,
2633
            detail::enable_if_t<std::is_constructible<
2634
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2635
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2636
                                  Args&&... args)
2637
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2638
  template <class... Args,
2639
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2640
                nullptr>
2641
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2642
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2643
2644
  template <class U, class... Args,
2645
            detail::enable_if_t<std::is_constructible<
2646
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2647
  constexpr explicit expected_storage_base(unexpect_t,
2648
                                           std::initializer_list<U> il,
2649
                                           Args&&... args)
2650
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2651
2652
  ~expected_storage_base() {
2653
    if (!m_has_val) {
2654
      m_unexpect.~unexpected<E>();
2655
    }
2656
  }
2657
2658
  union {
2659
    T m_val;
2660
    unexpected<E> m_unexpect;
2661
    char m_no_init;
2662
  };
2663
  bool m_has_val;
2664
};
2665
2666
// E is trivial, T is not.
2667
template <class T, class E>
2668
struct expected_storage_base<T, E, false, true> {
2669
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2670
0
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2671
2672
  template <class... Args,
2673
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2674
                nullptr>
2675
  constexpr expected_storage_base(in_place_t, Args&&... args)
2676
258k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2676
91.4k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2676
167k
      : 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_
2677
2678
  template <class U, class... Args,
2679
            detail::enable_if_t<std::is_constructible<
2680
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2681
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2682
                                  Args&&... args)
2683
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2684
  template <class... Args,
2685
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2686
                nullptr>
2687
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2688
64.5k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2688
30.4k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2688
34.1k
      : 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_
2689
2690
  template <class U, class... Args,
2691
            detail::enable_if_t<std::is_constructible<
2692
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2693
  constexpr explicit expected_storage_base(unexpect_t,
2694
                                           std::initializer_list<U> il,
2695
                                           Args&&... args)
2696
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2697
2698
323k
  ~expected_storage_base() {
2699
323k
    if (m_has_val) {
2700
258k
      m_val.~T();
2701
258k
    }
2702
323k
  }
tl::detail::expected_storage_base<ada::url, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2698
121k
  ~expected_storage_base() {
2699
121k
    if (m_has_val) {
2700
91.4k
      m_val.~T();
2701
91.4k
    }
2702
121k
  }
tl::detail::expected_storage_base<ada::url_aggregator, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2698
201k
  ~expected_storage_base() {
2699
201k
    if (m_has_val) {
2700
167k
      m_val.~T();
2701
167k
    }
2702
201k
  }
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()
2703
  union {
2704
    T m_val;
2705
    unexpected<E> m_unexpect;
2706
    char m_no_init;
2707
  };
2708
  bool m_has_val;
2709
};
2710
2711
// `T` is `void`, `E` is trivially-destructible
2712
template <class E>
2713
struct expected_storage_base<void, E, false, true> {
2714
#if __GNUC__ <= 5
2715
// no constexpr for GCC 4/5 bug
2716
#else
2717
  TL_EXPECTED_MSVC2015_CONSTEXPR
2718
#endif
2719
  expected_storage_base() : m_has_val(true) {}
2720
2721
  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2722
2723
  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2724
2725
  template <class... Args,
2726
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2727
                nullptr>
2728
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2729
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2730
2731
  template <class U, class... Args,
2732
            detail::enable_if_t<std::is_constructible<
2733
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2734
  constexpr explicit expected_storage_base(unexpect_t,
2735
                                           std::initializer_list<U> il,
2736
                                           Args&&... args)
2737
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2738
2739
  ~expected_storage_base() = default;
2740
  struct dummy {};
2741
  union {
2742
    unexpected<E> m_unexpect;
2743
    dummy m_val;
2744
  };
2745
  bool m_has_val;
2746
};
2747
2748
// `T` is `void`, `E` is not trivially-destructible
2749
template <class E>
2750
struct expected_storage_base<void, E, false, false> {
2751
  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2752
  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2753
2754
  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2755
2756
  template <class... Args,
2757
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2758
                nullptr>
2759
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2760
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2761
2762
  template <class U, class... Args,
2763
            detail::enable_if_t<std::is_constructible<
2764
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2765
  constexpr explicit expected_storage_base(unexpect_t,
2766
                                           std::initializer_list<U> il,
2767
                                           Args&&... args)
2768
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2769
2770
  ~expected_storage_base() {
2771
    if (!m_has_val) {
2772
      m_unexpect.~unexpected<E>();
2773
    }
2774
  }
2775
2776
  union {
2777
    unexpected<E> m_unexpect;
2778
    char m_dummy;
2779
  };
2780
  bool m_has_val;
2781
};
2782
2783
// This base class provides some handy member functions which can be used in
2784
// further derived classes
2785
template <class T, class E>
2786
struct expected_operations_base : expected_storage_base<T, E> {
2787
  using expected_storage_base<T, E>::expected_storage_base;
2788
2789
  template <class... Args>
2790
  void construct(Args&&... args) noexcept {
2791
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2792
    this->m_has_val = true;
2793
  }
2794
2795
  template <class Rhs>
2796
  // NOLINTNEXTLINE(bugprone-exception-escape)
2797
0
  void construct_with(Rhs&& rhs) noexcept {
2798
0
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2799
0
    this->m_has_val = true;
2800
0
  }
2801
2802
  template <class... Args>
2803
0
  void construct_error(Args&&... args) noexcept {
2804
0
    new (std::addressof(this->m_unexpect))
2805
0
        unexpected<E>(std::forward<Args>(args)...);
2806
0
    this->m_has_val = false;
2807
0
  }
2808
2809
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2810
2811
  // These assign overloads ensure that the most efficient assignment
2812
  // implementation is used while maintaining the strong exception guarantee.
2813
  // The problematic case is where rhs has a value, but *this does not.
2814
  //
2815
  // This overload handles the case where we can just copy-construct `T`
2816
  // directly into place without throwing.
2817
  template <class U = T,
2818
            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>* =
2819
                nullptr>
2820
  void assign(const expected_operations_base& rhs) noexcept {
2821
    if (!this->m_has_val && rhs.m_has_val) {
2822
      geterr().~unexpected<E>();
2823
      construct(rhs.get());
2824
    } else {
2825
      assign_common(rhs);
2826
    }
2827
  }
2828
2829
  // This overload handles the case where we can attempt to create a copy of
2830
  // `T`, then no-throw move it into place if the copy was successful.
2831
  template <class U = T,
2832
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2833
                                std::is_nothrow_move_constructible<U>::value>* =
2834
                nullptr>
2835
  void assign(const expected_operations_base& rhs) noexcept {
2836
    if (!this->m_has_val && rhs.m_has_val) {
2837
      T tmp = rhs.get();
2838
      geterr().~unexpected<E>();
2839
      construct(std::move(tmp));
2840
    } else {
2841
      assign_common(rhs);
2842
    }
2843
  }
2844
2845
  // This overload is the worst-case, where we have to move-construct the
2846
  // unexpected value into temporary storage, then try to copy the T into place.
2847
  // If the construction succeeds, then everything is fine, but if it throws,
2848
  // then we move the old unexpected value back into place before rethrowing the
2849
  // exception.
2850
  template <class U = T,
2851
            detail::enable_if_t<
2852
                !std::is_nothrow_copy_constructible<U>::value &&
2853
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2854
  void assign(const expected_operations_base& rhs) {
2855
    if (!this->m_has_val && rhs.m_has_val) {
2856
      auto tmp = std::move(geterr());
2857
      geterr().~unexpected<E>();
2858
2859
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2860
      try {
2861
        construct(rhs.get());
2862
      } catch (...) {
2863
        geterr() = std::move(tmp);
2864
        throw;
2865
      }
2866
#else
2867
      construct(rhs.get());
2868
#endif
2869
    } else {
2870
      assign_common(rhs);
2871
    }
2872
  }
2873
2874
  // These overloads do the same as above, but for rvalues
2875
  template <class U = T,
2876
            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>* =
2877
                nullptr>
2878
  void assign(expected_operations_base&& rhs) noexcept {
2879
    if (!this->m_has_val && rhs.m_has_val) {
2880
      geterr().~unexpected<E>();
2881
      construct(std::move(rhs).get());
2882
    } else {
2883
      assign_common(std::move(rhs));
2884
    }
2885
  }
2886
2887
  template <class U = T,
2888
            detail::enable_if_t<
2889
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2890
  void assign(expected_operations_base&& rhs) {
2891
    if (!this->m_has_val && rhs.m_has_val) {
2892
      auto tmp = std::move(geterr());
2893
      geterr().~unexpected<E>();
2894
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2895
      try {
2896
        construct(std::move(rhs).get());
2897
      } catch (...) {
2898
        geterr() = std::move(tmp);
2899
        throw;
2900
      }
2901
#else
2902
      construct(std::move(rhs).get());
2903
#endif
2904
    } else {
2905
      assign_common(std::move(rhs));
2906
    }
2907
  }
2908
2909
#else
2910
2911
  // If exceptions are disabled then we can just copy-construct
2912
  void assign(const expected_operations_base& rhs) noexcept {
2913
    if (!this->m_has_val && rhs.m_has_val) {
2914
      geterr().~unexpected<E>();
2915
      construct(rhs.get());
2916
    } else {
2917
      assign_common(rhs);
2918
    }
2919
  }
2920
2921
  void assign(expected_operations_base&& rhs) noexcept {
2922
    if (!this->m_has_val && rhs.m_has_val) {
2923
      geterr().~unexpected<E>();
2924
      construct(std::move(rhs).get());
2925
    } else {
2926
      assign_common(std::move(rhs));
2927
    }
2928
  }
2929
2930
#endif
2931
2932
  // The common part of move/copy assigning
2933
  template <class Rhs>
2934
  void assign_common(Rhs&& rhs) {
2935
    if (this->m_has_val) {
2936
      if (rhs.m_has_val) {
2937
        get() = std::forward<Rhs>(rhs).get();
2938
      } else {
2939
        destroy_val();
2940
        construct_error(std::forward<Rhs>(rhs).geterr());
2941
      }
2942
    } else {
2943
      if (!rhs.m_has_val) {
2944
        geterr() = std::forward<Rhs>(rhs).geterr();
2945
      }
2946
    }
2947
  }
2948
2949
0
  bool has_value() const { return this->m_has_val; }
2950
2951
  TL_EXPECTED_11_CONSTEXPR T& get() & { return this->m_val; }
2952
0
  constexpr const T& get() const& { return this->m_val; }
2953
  TL_EXPECTED_11_CONSTEXPR T&& get() && { return std::move(this->m_val); }
2954
#ifndef TL_EXPECTED_NO_CONSTRR
2955
  constexpr const T&& get() const&& { return std::move(this->m_val); }
2956
#endif
2957
2958
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2959
    return this->m_unexpect;
2960
  }
2961
0
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2962
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2963
    return std::move(this->m_unexpect);
2964
  }
2965
#ifndef TL_EXPECTED_NO_CONSTRR
2966
  constexpr const unexpected<E>&& geterr() const&& {
2967
    return std::move(this->m_unexpect);
2968
  }
2969
#endif
2970
2971
  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2972
};
2973
2974
// This base class provides some handy member functions which can be used in
2975
// further derived classes
2976
template <class E>
2977
struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2978
  using expected_storage_base<void, E>::expected_storage_base;
2979
2980
  template <class... Args>
2981
  void construct() noexcept {
2982
    this->m_has_val = true;
2983
  }
2984
2985
  // This function doesn't use its argument, but needs it so that code in
2986
  // levels above this can work independently of whether T is void
2987
  template <class Rhs>
2988
  void construct_with(Rhs&&) noexcept {
2989
    this->m_has_val = true;
2990
  }
2991
2992
  template <class... Args>
2993
  void construct_error(Args&&... args) noexcept {
2994
    new (std::addressof(this->m_unexpect))
2995
        unexpected<E>(std::forward<Args>(args)...);
2996
    this->m_has_val = false;
2997
  }
2998
2999
  template <class Rhs>
3000
  void assign(Rhs&& rhs) noexcept {
3001
    if (!this->m_has_val) {
3002
      if (rhs.m_has_val) {
3003
        geterr().~unexpected<E>();
3004
        construct();
3005
      } else {
3006
        geterr() = std::forward<Rhs>(rhs).geterr();
3007
      }
3008
    } else {
3009
      if (!rhs.m_has_val) {
3010
        construct_error(std::forward<Rhs>(rhs).geterr());
3011
      }
3012
    }
3013
  }
3014
3015
  bool has_value() const { return this->m_has_val; }
3016
3017
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
3018
    return this->m_unexpect;
3019
  }
3020
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
3021
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
3022
    return std::move(this->m_unexpect);
3023
  }
3024
#ifndef TL_EXPECTED_NO_CONSTRR
3025
  constexpr const unexpected<E>&& geterr() const&& {
3026
    return std::move(this->m_unexpect);
3027
  }
3028
#endif
3029
3030
  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
3031
    // no-op
3032
  }
3033
};
3034
3035
// This class manages conditionally having a trivial copy constructor
3036
// This specialization is for when T and E are trivially copy constructible
3037
template <class T, class E,
3038
          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(
3039
                                   T)>::value &&
3040
                 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
3041
struct expected_copy_base : expected_operations_base<T, E> {
3042
  using expected_operations_base<T, E>::expected_operations_base;
3043
};
3044
3045
// This specialization is for when T or E are not trivially copy constructible
3046
template <class T, class E>
3047
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
3048
  using expected_operations_base<T, E>::expected_operations_base;
3049
3050
  expected_copy_base() = default;
3051
  expected_copy_base(const expected_copy_base& rhs)
3052
0
      : expected_operations_base<T, E>(no_init) {
3053
0
    if (rhs.has_value()) {
3054
0
      this->construct_with(rhs);
3055
0
    } else {
3056
0
      this->construct_error(rhs.geterr());
3057
0
    }
3058
0
  }
3059
3060
  expected_copy_base(expected_copy_base&& rhs) = default;
3061
  expected_copy_base& operator=(const expected_copy_base& rhs) = default;
3062
  expected_copy_base& operator=(expected_copy_base&& rhs) = default;
3063
};
3064
3065
// This class manages conditionally having a trivial move constructor
3066
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3067
// doesn't implement an analogue to std::is_trivially_move_constructible. We
3068
// have to make do with a non-trivial move constructor even if T is trivially
3069
// move constructible
3070
#ifndef TL_EXPECTED_GCC49
3071
template <class T, class E,
3072
          bool =
3073
              is_void_or<T, std::is_trivially_move_constructible<T>>::value &&
3074
              std::is_trivially_move_constructible<E>::value>
3075
struct expected_move_base : expected_copy_base<T, E> {
3076
  using expected_copy_base<T, E>::expected_copy_base;
3077
};
3078
#else
3079
template <class T, class E, bool = false>
3080
struct expected_move_base;
3081
#endif
3082
template <class T, class E>
3083
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
3084
  using expected_copy_base<T, E>::expected_copy_base;
3085
3086
  expected_move_base() = default;
3087
0
  expected_move_base(const expected_move_base& rhs) = default;
3088
3089
  expected_move_base(expected_move_base&& rhs) noexcept(
3090
      std::is_nothrow_move_constructible<T>::value)
3091
      : expected_copy_base<T, E>(no_init) {
3092
    if (rhs.has_value()) {
3093
      this->construct_with(std::move(rhs));
3094
    } else {
3095
      this->construct_error(std::move(rhs.geterr()));
3096
    }
3097
  }
3098
  expected_move_base& operator=(const expected_move_base& rhs) = default;
3099
  expected_move_base& operator=(expected_move_base&& rhs) = default;
3100
};
3101
3102
// This class manages conditionally having a trivial copy assignment operator
3103
template <
3104
    class T, class E,
3105
    bool =
3106
        is_void_or<
3107
            T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
3108
                           TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
3109
                           TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value &&
3110
        TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value &&
3111
        TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value &&
3112
        TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
3113
struct expected_copy_assign_base : expected_move_base<T, E> {
3114
  using expected_move_base<T, E>::expected_move_base;
3115
};
3116
3117
template <class T, class E>
3118
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
3119
  using expected_move_base<T, E>::expected_move_base;
3120
3121
  expected_copy_assign_base() = default;
3122
0
  expected_copy_assign_base(const expected_copy_assign_base& rhs) = default;
3123
3124
  expected_copy_assign_base(expected_copy_assign_base&& rhs) = default;
3125
  expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
3126
    this->assign(rhs);
3127
    return *this;
3128
  }
3129
  expected_copy_assign_base& operator=(expected_copy_assign_base&& rhs) =
3130
      default;
3131
};
3132
3133
// This class manages conditionally having a trivial move assignment operator
3134
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3135
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
3136
// to make do with a non-trivial move assignment operator even if T is trivially
3137
// move assignable
3138
#ifndef TL_EXPECTED_GCC49
3139
template <
3140
    class T, class E,
3141
    bool = is_void_or<
3142
               T, conjunction<std::is_trivially_destructible<T>,
3143
                              std::is_trivially_move_constructible<T>,
3144
                              std::is_trivially_move_assignable<T>>>::value &&
3145
           std::is_trivially_destructible<E>::value &&
3146
           std::is_trivially_move_constructible<E>::value &&
3147
           std::is_trivially_move_assignable<E>::value>
3148
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
3149
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3150
};
3151
#else
3152
template <class T, class E, bool = false>
3153
struct expected_move_assign_base;
3154
#endif
3155
3156
template <class T, class E>
3157
struct expected_move_assign_base<T, E, false>
3158
    : expected_copy_assign_base<T, E> {
3159
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3160
3161
  expected_move_assign_base() = default;
3162
0
  expected_move_assign_base(const expected_move_assign_base& rhs) = default;
3163
3164
  expected_move_assign_base(expected_move_assign_base&& rhs) = default;
3165
3166
  expected_move_assign_base& operator=(const expected_move_assign_base& rhs) =
3167
      default;
3168
3169
  expected_move_assign_base&
3170
  operator=(expected_move_assign_base&& rhs) noexcept(
3171
      std::is_nothrow_move_constructible<T>::value &&
3172
      std::is_nothrow_move_assignable<T>::value) {
3173
    this->assign(std::move(rhs));
3174
    return *this;
3175
  }
3176
};
3177
3178
// expected_delete_ctor_base will conditionally delete copy and move
3179
// constructors depending on whether T is copy/move constructible
3180
template <class T, class E,
3181
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3182
                             std::is_copy_constructible<E>::value),
3183
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3184
                             std::is_move_constructible<E>::value)>
3185
struct expected_delete_ctor_base {
3186
  expected_delete_ctor_base() = default;
3187
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3188
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3189
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3190
      default;
3191
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3192
      default;
3193
};
3194
3195
template <class T, class E>
3196
struct expected_delete_ctor_base<T, E, true, false> {
3197
  expected_delete_ctor_base() = default;
3198
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3199
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3200
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3201
      default;
3202
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3203
      default;
3204
};
3205
3206
template <class T, class E>
3207
struct expected_delete_ctor_base<T, E, false, true> {
3208
  expected_delete_ctor_base() = default;
3209
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3210
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3211
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3212
      default;
3213
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3214
      default;
3215
};
3216
3217
template <class T, class E>
3218
struct expected_delete_ctor_base<T, E, false, false> {
3219
  expected_delete_ctor_base() = default;
3220
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3221
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3222
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3223
      default;
3224
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3225
      default;
3226
};
3227
3228
// expected_delete_assign_base will conditionally delete copy and move
3229
// constructors depending on whether T and E are copy/move constructible +
3230
// assignable
3231
template <class T, class E,
3232
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3233
                             std::is_copy_constructible<E>::value &&
3234
                             is_copy_assignable_or_void<T>::value &&
3235
                             std::is_copy_assignable<E>::value),
3236
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3237
                             std::is_move_constructible<E>::value &&
3238
                             is_move_assignable_or_void<T>::value &&
3239
                             std::is_move_assignable<E>::value)>
3240
struct expected_delete_assign_base {
3241
  expected_delete_assign_base() = default;
3242
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3243
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3244
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3245
      default;
3246
  expected_delete_assign_base& operator=(
3247
      expected_delete_assign_base&&) noexcept = default;
3248
};
3249
3250
template <class T, class E>
3251
struct expected_delete_assign_base<T, E, true, false> {
3252
  expected_delete_assign_base() = default;
3253
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3254
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3255
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3256
      default;
3257
  expected_delete_assign_base& operator=(
3258
      expected_delete_assign_base&&) noexcept = delete;
3259
};
3260
3261
template <class T, class E>
3262
struct expected_delete_assign_base<T, E, false, true> {
3263
  expected_delete_assign_base() = default;
3264
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3265
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3266
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3267
      delete;
3268
  expected_delete_assign_base& operator=(
3269
      expected_delete_assign_base&&) noexcept = default;
3270
};
3271
3272
template <class T, class E>
3273
struct expected_delete_assign_base<T, E, false, false> {
3274
  expected_delete_assign_base() = default;
3275
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3276
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3277
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3278
      delete;
3279
  expected_delete_assign_base& operator=(
3280
      expected_delete_assign_base&&) noexcept = delete;
3281
};
3282
3283
// This is needed to be able to construct the expected_default_ctor_base which
3284
// follows, while still conditionally deleting the default constructor.
3285
struct default_constructor_tag {
3286
  explicit constexpr default_constructor_tag() = default;
3287
};
3288
3289
// expected_default_ctor_base will ensure that expected has a deleted default
3290
// consturctor if T is not default constructible.
3291
// This specialization is for when T is default constructible
3292
template <class T, class E,
3293
          bool Enable =
3294
              std::is_default_constructible<T>::value || std::is_void<T>::value>
3295
struct expected_default_ctor_base {
3296
  constexpr expected_default_ctor_base() noexcept = default;
3297
  constexpr expected_default_ctor_base(
3298
      expected_default_ctor_base const&) noexcept = default;
3299
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3300
      default;
3301
  expected_default_ctor_base& operator=(
3302
      expected_default_ctor_base const&) noexcept = default;
3303
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3304
      default;
3305
3306
323k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
121k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_aggregator, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
201k
  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)
3307
};
3308
3309
// This specialization is for when T is not default constructible
3310
template <class T, class E>
3311
struct expected_default_ctor_base<T, E, false> {
3312
  constexpr expected_default_ctor_base() noexcept = delete;
3313
  constexpr expected_default_ctor_base(
3314
      expected_default_ctor_base const&) noexcept = default;
3315
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3316
      default;
3317
  expected_default_ctor_base& operator=(
3318
      expected_default_ctor_base const&) noexcept = default;
3319
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3320
      default;
3321
3322
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3323
};
3324
}  // namespace detail
3325
3326
template <class E>
3327
class bad_expected_access : public std::exception {
3328
 public:
3329
0
  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3330
3331
0
  virtual const char* what() const noexcept override {
3332
0
    return "Bad expected access";
3333
0
  }
3334
3335
  const E& error() const& { return m_val; }
3336
  E& error() & { return m_val; }
3337
  const E&& error() const&& { return std::move(m_val); }
3338
  E&& error() && { return std::move(m_val); }
3339
3340
 private:
3341
  E m_val;
3342
};
3343
3344
/// An `expected<T, E>` object is an object that contains the storage for
3345
/// another object and manages the lifetime of this contained object `T`.
3346
/// Alternatively it could contain the storage for another unexpected object
3347
/// `E`. The contained object may not be initialized after the expected object
3348
/// has been initialized, and may not be destroyed before the expected object
3349
/// has been destroyed. The initialization state of the contained object is
3350
/// tracked by the expected object.
3351
template <class T, class E>
3352
class expected : private detail::expected_move_assign_base<T, E>,
3353
                 private detail::expected_delete_ctor_base<T, E>,
3354
                 private detail::expected_delete_assign_base<T, E>,
3355
                 private detail::expected_default_ctor_base<T, E> {
3356
  static_assert(!std::is_reference<T>::value, "T must not be a reference");
3357
  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3358
                "T must not be in_place_t");
3359
  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3360
                "T must not be unexpect_t");
3361
  static_assert(
3362
      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3363
      "T must not be unexpected<E>");
3364
  static_assert(!std::is_reference<E>::value, "E must not be a reference");
3365
3366
2.15M
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<ada::url, ada::errors>::valptr()
Line
Count
Source
3366
863k
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<ada::url_aggregator, ada::errors>::valptr()
Line
Count
Source
3366
1.28M
  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()
3367
  const T* valptr() const { return std::addressof(this->m_val); }
3368
  unexpected<E>* errptr() { return std::addressof(this->m_unexpect); }
3369
  const unexpected<E>* errptr() const {
3370
    return std::addressof(this->m_unexpect);
3371
  }
3372
3373
  template <class U = T,
3374
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3375
191k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
191k
    return this->m_val;
3377
191k
  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3375
71.0k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
71.0k
    return this->m_val;
3377
71.0k
  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3375
120k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
120k
    return this->m_val;
3377
120k
  }
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
3378
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()
3379
3380
  template <class U = T,
3381
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3382
  constexpr const U& val() const {
3383
    return this->m_val;
3384
  }
3385
  constexpr const unexpected<E>& err() const { return this->m_unexpect; }
3386
3387
  using impl_base = detail::expected_move_assign_base<T, E>;
3388
  using ctor_base = detail::expected_default_ctor_base<T, E>;
3389
3390
 public:
3391
  typedef T value_type;
3392
  typedef E error_type;
3393
  typedef unexpected<E> unexpected_type;
3394
3395
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3396
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3397
  template <class F>
3398
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & {
3399
    return and_then_impl(*this, std::forward<F>(f));
3400
  }
3401
  template <class F>
3402
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && {
3403
    return and_then_impl(std::move(*this), std::forward<F>(f));
3404
  }
3405
  template <class F>
3406
  constexpr auto and_then(F&& f) const& {
3407
    return and_then_impl(*this, std::forward<F>(f));
3408
  }
3409
3410
#ifndef TL_EXPECTED_NO_CONSTRR
3411
  template <class F>
3412
  constexpr auto and_then(F&& f) const&& {
3413
    return and_then_impl(std::move(*this), std::forward<F>(f));
3414
  }
3415
#endif
3416
3417
#else
3418
  template <class F>
3419
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & -> decltype(and_then_impl(
3420
      std::declval<expected&>(), std::forward<F>(f))) {
3421
    return and_then_impl(*this, std::forward<F>(f));
3422
  }
3423
  template <class F>
3424
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && -> decltype(and_then_impl(
3425
      std::declval<expected&&>(), std::forward<F>(f))) {
3426
    return and_then_impl(std::move(*this), std::forward<F>(f));
3427
  }
3428
  template <class F>
3429
  constexpr auto and_then(F&& f) const& -> decltype(and_then_impl(
3430
      std::declval<expected const&>(), std::forward<F>(f))) {
3431
    return and_then_impl(*this, std::forward<F>(f));
3432
  }
3433
3434
#ifndef TL_EXPECTED_NO_CONSTRR
3435
  template <class F>
3436
  constexpr auto and_then(F&& f) const&& -> decltype(and_then_impl(
3437
      std::declval<expected const&&>(), std::forward<F>(f))) {
3438
    return and_then_impl(std::move(*this), std::forward<F>(f));
3439
  }
3440
#endif
3441
#endif
3442
3443
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3444
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3445
  template <class F>
3446
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) & {
3447
    return expected_map_impl(*this, std::forward<F>(f));
3448
  }
3449
  template <class F>
3450
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) && {
3451
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3452
  }
3453
  template <class F>
3454
  constexpr auto map(F&& f) const& {
3455
    return expected_map_impl(*this, std::forward<F>(f));
3456
  }
3457
  template <class F>
3458
  constexpr auto map(F&& f) const&& {
3459
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3460
  }
3461
#else
3462
  template <class F>
3463
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3464
      std::declval<expected&>(), std::declval<F&&>())) map(F&& f) & {
3465
    return expected_map_impl(*this, std::forward<F>(f));
3466
  }
3467
  template <class F>
3468
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3469
                                                      std::declval<F&&>()))
3470
  map(F&& f) && {
3471
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3472
  }
3473
  template <class F>
3474
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3475
                                       std::declval<F&&>()))
3476
  map(F&& f) const& {
3477
    return expected_map_impl(*this, std::forward<F>(f));
3478
  }
3479
3480
#ifndef TL_EXPECTED_NO_CONSTRR
3481
  template <class F>
3482
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3483
                                       std::declval<F&&>())) map(F&& f)
3484
      const&& {
3485
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3486
  }
3487
#endif
3488
#endif
3489
3490
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3491
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3492
  template <class F>
3493
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) & {
3494
    return expected_map_impl(*this, std::forward<F>(f));
3495
  }
3496
  template <class F>
3497
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) && {
3498
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3499
  }
3500
  template <class F>
3501
  constexpr auto transform(F&& f) const& {
3502
    return expected_map_impl(*this, std::forward<F>(f));
3503
  }
3504
  template <class F>
3505
  constexpr auto transform(F&& f) const&& {
3506
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3507
  }
3508
#else
3509
  template <class F>
3510
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3511
      std::declval<expected&>(), std::declval<F&&>())) transform(F&& f) & {
3512
    return expected_map_impl(*this, std::forward<F>(f));
3513
  }
3514
  template <class F>
3515
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3516
                                                      std::declval<F&&>()))
3517
  transform(F&& f) && {
3518
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3519
  }
3520
  template <class F>
3521
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3522
                                       std::declval<F&&>()))
3523
  transform(F&& f) const& {
3524
    return expected_map_impl(*this, std::forward<F>(f));
3525
  }
3526
3527
#ifndef TL_EXPECTED_NO_CONSTRR
3528
  template <class F>
3529
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3530
                                       std::declval<F&&>())) transform(F&& f)
3531
      const&& {
3532
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3533
  }
3534
#endif
3535
#endif
3536
3537
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3538
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3539
  template <class F>
3540
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) & {
3541
    return map_error_impl(*this, std::forward<F>(f));
3542
  }
3543
  template <class F>
3544
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) && {
3545
    return map_error_impl(std::move(*this), std::forward<F>(f));
3546
  }
3547
  template <class F>
3548
  constexpr auto map_error(F&& f) const& {
3549
    return map_error_impl(*this, std::forward<F>(f));
3550
  }
3551
  template <class F>
3552
  constexpr auto map_error(F&& f) const&& {
3553
    return map_error_impl(std::move(*this), std::forward<F>(f));
3554
  }
3555
#else
3556
  template <class F>
3557
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3558
      std::declval<expected&>(), std::declval<F&&>())) map_error(F&& f) & {
3559
    return map_error_impl(*this, std::forward<F>(f));
3560
  }
3561
  template <class F>
3562
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3563
                                                   std::declval<F&&>()))
3564
  map_error(F&& f) && {
3565
    return map_error_impl(std::move(*this), std::forward<F>(f));
3566
  }
3567
  template <class F>
3568
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3569
                                    std::declval<F&&>()))
3570
  map_error(F&& f) const& {
3571
    return map_error_impl(*this, std::forward<F>(f));
3572
  }
3573
3574
#ifndef TL_EXPECTED_NO_CONSTRR
3575
  template <class F>
3576
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3577
                                    std::declval<F&&>())) map_error(F&& f)
3578
      const&& {
3579
    return map_error_impl(std::move(*this), std::forward<F>(f));
3580
  }
3581
#endif
3582
#endif
3583
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3584
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3585
  template <class F>
3586
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) & {
3587
    return map_error_impl(*this, std::forward<F>(f));
3588
  }
3589
  template <class F>
3590
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) && {
3591
    return map_error_impl(std::move(*this), std::forward<F>(f));
3592
  }
3593
  template <class F>
3594
  constexpr auto transform_error(F&& f) const& {
3595
    return map_error_impl(*this, std::forward<F>(f));
3596
  }
3597
  template <class F>
3598
  constexpr auto transform_error(F&& f) const&& {
3599
    return map_error_impl(std::move(*this), std::forward<F>(f));
3600
  }
3601
#else
3602
  template <class F>
3603
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3604
      std::declval<expected&>(),
3605
      std::declval<F&&>())) transform_error(F&& f) & {
3606
    return map_error_impl(*this, std::forward<F>(f));
3607
  }
3608
  template <class F>
3609
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3610
                                                   std::declval<F&&>()))
3611
  transform_error(F&& f) && {
3612
    return map_error_impl(std::move(*this), std::forward<F>(f));
3613
  }
3614
  template <class F>
3615
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3616
                                    std::declval<F&&>()))
3617
  transform_error(F&& f) const& {
3618
    return map_error_impl(*this, std::forward<F>(f));
3619
  }
3620
3621
#ifndef TL_EXPECTED_NO_CONSTRR
3622
  template <class F>
3623
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3624
                                    std::declval<F&&>())) transform_error(F&& f)
3625
      const&& {
3626
    return map_error_impl(std::move(*this), std::forward<F>(f));
3627
  }
3628
#endif
3629
#endif
3630
  template <class F>
3631
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) & {
3632
    return or_else_impl(*this, std::forward<F>(f));
3633
  }
3634
3635
  template <class F>
3636
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) && {
3637
    return or_else_impl(std::move(*this), std::forward<F>(f));
3638
  }
3639
3640
  template <class F>
3641
  expected constexpr or_else(F&& f) const& {
3642
    return or_else_impl(*this, std::forward<F>(f));
3643
  }
3644
3645
#ifndef TL_EXPECTED_NO_CONSTRR
3646
  template <class F>
3647
  expected constexpr or_else(F&& f) const&& {
3648
    return or_else_impl(std::move(*this), std::forward<F>(f));
3649
  }
3650
#endif
3651
  constexpr expected() = default;
3652
0
  constexpr expected(const expected& rhs) = default;
3653
  constexpr expected(expected&& rhs) = default;
3654
  expected& operator=(const expected& rhs) = default;
3655
  expected& operator=(expected&& rhs) = default;
3656
3657
  template <class... Args,
3658
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
3659
                nullptr>
3660
  constexpr expected(in_place_t, Args&&... args)
3661
258k
      : impl_base(in_place, std::forward<Args>(args)...),
3662
258k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3661
91.4k
      : impl_base(in_place, std::forward<Args>(args)...),
3662
91.4k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3661
167k
      : impl_base(in_place, std::forward<Args>(args)...),
3662
167k
        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_
3663
3664
  template <class U, class... Args,
3665
            detail::enable_if_t<std::is_constructible<
3666
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3667
  constexpr expected(in_place_t, std::initializer_list<U> il, Args&&... args)
3668
      : impl_base(in_place, il, std::forward<Args>(args)...),
3669
        ctor_base(detail::default_constructor_tag{}) {}
3670
3671
  template <
3672
      class G = E,
3673
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3674
      detail::enable_if_t<!std::is_convertible<const G&, E>::value>* = nullptr>
3675
  explicit constexpr expected(const unexpected<G>& e)
3676
      : impl_base(unexpect, e.value()),
3677
        ctor_base(detail::default_constructor_tag{}) {}
3678
3679
  template <
3680
      class G = E,
3681
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3682
      detail::enable_if_t<std::is_convertible<const G&, E>::value>* = nullptr>
3683
  constexpr expected(unexpected<G> const& e)
3684
      : impl_base(unexpect, e.value()),
3685
        ctor_base(detail::default_constructor_tag{}) {}
3686
3687
  template <
3688
      class G = E,
3689
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3690
      detail::enable_if_t<!std::is_convertible<G&&, E>::value>* = nullptr>
3691
  explicit constexpr expected(unexpected<G>&& e) noexcept(
3692
      std::is_nothrow_constructible<E, G&&>::value)
3693
      : impl_base(unexpect, std::move(e.value())),
3694
        ctor_base(detail::default_constructor_tag{}) {}
3695
3696
  template <
3697
      class G = E,
3698
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3699
      detail::enable_if_t<std::is_convertible<G&&, E>::value>* = nullptr>
3700
  constexpr expected(unexpected<G>&& e) noexcept(
3701
      std::is_nothrow_constructible<E, G&&>::value)
3702
64.5k
      : impl_base(unexpect, std::move(e.value())),
3703
64.5k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3702
30.4k
      : impl_base(unexpect, std::move(e.value())),
3703
30.4k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3702
34.1k
      : impl_base(unexpect, std::move(e.value())),
3703
34.1k
        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
3704
3705
  template <class... Args,
3706
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
3707
                nullptr>
3708
  constexpr explicit expected(unexpect_t, Args&&... args)
3709
      : impl_base(unexpect, std::forward<Args>(args)...),
3710
        ctor_base(detail::default_constructor_tag{}) {}
3711
3712
  template <class U, class... Args,
3713
            detail::enable_if_t<std::is_constructible<
3714
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3715
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3716
                              Args&&... args)
3717
      : impl_base(unexpect, il, std::forward<Args>(args)...),
3718
        ctor_base(detail::default_constructor_tag{}) {}
3719
3720
  template <class U, class G,
3721
            detail::enable_if_t<!(std::is_convertible<U const&, T>::value &&
3722
                                  std::is_convertible<G const&, E>::value)>* =
3723
                nullptr,
3724
            detail::expected_enable_from_other<T, E, U, G, const U&,
3725
                                               const G&>* = nullptr>
3726
  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3727
      : ctor_base(detail::default_constructor_tag{}) {
3728
    if (rhs.has_value()) {
3729
      this->construct(*rhs);
3730
    } else {
3731
      this->construct_error(rhs.error());
3732
    }
3733
  }
3734
3735
  template <
3736
      class U, class G,
3737
      detail::enable_if_t<(std::is_convertible<U const&, T>::value &&
3738
                           std::is_convertible<G const&, E>::value)>* = nullptr,
3739
      detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* =
3740
          nullptr>
3741
  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3742
      : ctor_base(detail::default_constructor_tag{}) {
3743
    if (rhs.has_value()) {
3744
      this->construct(*rhs);
3745
    } else {
3746
      this->construct_error(rhs.error());
3747
    }
3748
  }
3749
3750
  template <
3751
      class U, class G,
3752
      detail::enable_if_t<!(std::is_convertible<U&&, T>::value &&
3753
                            std::is_convertible<G&&, E>::value)>* = nullptr,
3754
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3755
  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3756
      : ctor_base(detail::default_constructor_tag{}) {
3757
    if (rhs.has_value()) {
3758
      this->construct(std::move(*rhs));
3759
    } else {
3760
      this->construct_error(std::move(rhs.error()));
3761
    }
3762
  }
3763
3764
  template <
3765
      class U, class G,
3766
      detail::enable_if_t<(std::is_convertible<U&&, T>::value &&
3767
                           std::is_convertible<G&&, E>::value)>* = nullptr,
3768
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3769
  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3770
      : ctor_base(detail::default_constructor_tag{}) {
3771
    if (rhs.has_value()) {
3772
      this->construct(std::move(*rhs));
3773
    } else {
3774
      this->construct_error(std::move(rhs.error()));
3775
    }
3776
  }
3777
3778
  template <class U = T,
3779
            detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr,
3780
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3781
  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3782
      : expected(in_place, std::forward<U>(v)) {}
3783
3784
  template <class U = T,
3785
            detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr,
3786
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3787
  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3788
258k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3788
91.4k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3788
167k
      : 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_
3789
3790
  template <
3791
      class U = T, class G = T,
3792
      detail::enable_if_t<std::is_nothrow_constructible<T, U&&>::value>* =
3793
          nullptr,
3794
      detail::enable_if_t<!std::is_void<G>::value>* = nullptr,
3795
      detail::enable_if_t<
3796
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3797
           !detail::conjunction<std::is_scalar<T>,
3798
                                std::is_same<T, detail::decay_t<U>>>::value &&
3799
           std::is_constructible<T, U>::value &&
3800
           std::is_assignable<G&, U>::value &&
3801
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3802
  expected& operator=(U&& v) {
3803
    if (has_value()) {
3804
      val() = std::forward<U>(v);
3805
    } else {
3806
      err().~unexpected<E>();
3807
      ::new (valptr()) T(std::forward<U>(v));
3808
      this->m_has_val = true;
3809
    }
3810
3811
    return *this;
3812
  }
3813
3814
  template <
3815
      class U = T, class G = T,
3816
      detail::enable_if_t<!std::is_nothrow_constructible<T, U&&>::value>* =
3817
          nullptr,
3818
      detail::enable_if_t<!std::is_void<U>::value>* = nullptr,
3819
      detail::enable_if_t<
3820
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3821
           !detail::conjunction<std::is_scalar<T>,
3822
                                std::is_same<T, detail::decay_t<U>>>::value &&
3823
           std::is_constructible<T, U>::value &&
3824
           std::is_assignable<G&, U>::value &&
3825
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3826
  expected& operator=(U&& v) {
3827
    if (has_value()) {
3828
      val() = std::forward<U>(v);
3829
    } else {
3830
      auto tmp = std::move(err());
3831
      err().~unexpected<E>();
3832
3833
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3834
      try {
3835
        ::new (valptr()) T(std::forward<U>(v));
3836
        this->m_has_val = true;
3837
      } catch (...) {
3838
        err() = std::move(tmp);
3839
        throw;
3840
      }
3841
#else
3842
      ::new (valptr()) T(std::forward<U>(v));
3843
      this->m_has_val = true;
3844
#endif
3845
    }
3846
3847
    return *this;
3848
  }
3849
3850
  template <class G = E,
3851
            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3852
                                std::is_assignable<G&, G>::value>* = nullptr>
3853
  expected& operator=(const unexpected<G>& rhs) {
3854
    if (!has_value()) {
3855
      err() = rhs;
3856
    } else {
3857
      this->destroy_val();
3858
      ::new (errptr()) unexpected<E>(rhs);
3859
      this->m_has_val = false;
3860
    }
3861
3862
    return *this;
3863
  }
3864
3865
  template <class G = E,
3866
            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3867
                                std::is_move_assignable<G>::value>* = nullptr>
3868
  expected& operator=(unexpected<G>&& rhs) noexcept {
3869
    if (!has_value()) {
3870
      err() = std::move(rhs);
3871
    } else {
3872
      this->destroy_val();
3873
      ::new (errptr()) unexpected<E>(std::move(rhs));
3874
      this->m_has_val = false;
3875
    }
3876
3877
    return *this;
3878
  }
3879
3880
  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3881
                               T, Args&&...>::value>* = nullptr>
3882
  void emplace(Args&&... args) {
3883
    if (has_value()) {
3884
      val().~T();
3885
    } else {
3886
      err().~unexpected<E>();
3887
      this->m_has_val = true;
3888
    }
3889
    ::new (valptr()) T(std::forward<Args>(args)...);
3890
  }
3891
3892
  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3893
                               T, Args&&...>::value>* = nullptr>
3894
  void emplace(Args&&... args) {
3895
    if (has_value()) {
3896
      val().~T();
3897
      ::new (valptr()) T(std::forward<Args>(args)...);
3898
    } else {
3899
      auto tmp = std::move(err());
3900
      err().~unexpected<E>();
3901
3902
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3903
      try {
3904
        ::new (valptr()) T(std::forward<Args>(args)...);
3905
        this->m_has_val = true;
3906
      } catch (...) {
3907
        err() = std::move(tmp);
3908
        throw;
3909
      }
3910
#else
3911
      ::new (valptr()) T(std::forward<Args>(args)...);
3912
      this->m_has_val = true;
3913
#endif
3914
    }
3915
  }
3916
3917
  template <class U, class... Args,
3918
            detail::enable_if_t<std::is_nothrow_constructible<
3919
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3920
  void emplace(std::initializer_list<U> il, Args&&... args) {
3921
    if (has_value()) {
3922
      T t(il, std::forward<Args>(args)...);
3923
      val() = std::move(t);
3924
    } else {
3925
      err().~unexpected<E>();
3926
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3927
      this->m_has_val = true;
3928
    }
3929
  }
3930
3931
  template <class U, class... Args,
3932
            detail::enable_if_t<!std::is_nothrow_constructible<
3933
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3934
  void emplace(std::initializer_list<U> il, Args&&... args) {
3935
    if (has_value()) {
3936
      T t(il, std::forward<Args>(args)...);
3937
      val() = std::move(t);
3938
    } else {
3939
      auto tmp = std::move(err());
3940
      err().~unexpected<E>();
3941
3942
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3943
      try {
3944
        ::new (valptr()) T(il, std::forward<Args>(args)...);
3945
        this->m_has_val = true;
3946
      } catch (...) {
3947
        err() = std::move(tmp);
3948
        throw;
3949
      }
3950
#else
3951
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3952
      this->m_has_val = true;
3953
#endif
3954
    }
3955
  }
3956
3957
 private:
3958
  using t_is_void = std::true_type;
3959
  using t_is_not_void = std::false_type;
3960
  using t_is_nothrow_move_constructible = std::true_type;
3961
  using move_constructing_t_can_throw = std::false_type;
3962
  using e_is_nothrow_move_constructible = std::true_type;
3963
  using move_constructing_e_can_throw = std::false_type;
3964
3965
  void swap_where_both_have_value(expected& /*rhs*/, t_is_void) noexcept {
3966
    // swapping void is a no-op
3967
  }
3968
3969
  void swap_where_both_have_value(expected& rhs, t_is_not_void) {
3970
    using std::swap;
3971
    swap(val(), rhs.val());
3972
  }
3973
3974
  void swap_where_only_one_has_value(expected& rhs, t_is_void) noexcept(
3975
      std::is_nothrow_move_constructible<E>::value) {
3976
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3977
    rhs.err().~unexpected_type();
3978
    std::swap(this->m_has_val, rhs.m_has_val);
3979
  }
3980
3981
  void swap_where_only_one_has_value(expected& rhs, t_is_not_void) {
3982
    swap_where_only_one_has_value_and_t_is_not_void(
3983
        rhs, typename std::is_nothrow_move_constructible<T>::type{},
3984
        typename std::is_nothrow_move_constructible<E>::type{});
3985
  }
3986
3987
  void swap_where_only_one_has_value_and_t_is_not_void(
3988
      expected& rhs, t_is_nothrow_move_constructible,
3989
      e_is_nothrow_move_constructible) noexcept {
3990
    auto temp = std::move(val());
3991
    val().~T();
3992
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3993
    rhs.err().~unexpected_type();
3994
    ::new (rhs.valptr()) T(std::move(temp));
3995
    std::swap(this->m_has_val, rhs.m_has_val);
3996
  }
3997
3998
  void swap_where_only_one_has_value_and_t_is_not_void(
3999
      expected& rhs, t_is_nothrow_move_constructible,
4000
      move_constructing_e_can_throw) {
4001
    auto temp = std::move(val());
4002
    val().~T();
4003
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
4004
    try {
4005
      ::new (errptr()) unexpected_type(std::move(rhs.err()));
4006
      rhs.err().~unexpected_type();
4007
      ::new (rhs.valptr()) T(std::move(temp));
4008
      std::swap(this->m_has_val, rhs.m_has_val);
4009
    } catch (...) {
4010
      val() = std::move(temp);
4011
      throw;
4012
    }
4013
#else
4014
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
4015
    rhs.err().~unexpected_type();
4016
    ::new (rhs.valptr()) T(std::move(temp));
4017
    std::swap(this->m_has_val, rhs.m_has_val);
4018
#endif
4019
  }
4020
4021
  void swap_where_only_one_has_value_and_t_is_not_void(
4022
      expected& rhs, move_constructing_t_can_throw,
4023
      e_is_nothrow_move_constructible) {
4024
    auto temp = std::move(rhs.err());
4025
    rhs.err().~unexpected_type();
4026
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
4027
    try {
4028
      ::new (rhs.valptr()) T(std::move(val()));
4029
      val().~T();
4030
      ::new (errptr()) unexpected_type(std::move(temp));
4031
      std::swap(this->m_has_val, rhs.m_has_val);
4032
    } catch (...) {
4033
      rhs.err() = std::move(temp);
4034
      throw;
4035
    }
4036
#else
4037
    ::new (rhs.valptr()) T(std::move(val()));
4038
    val().~T();
4039
    ::new (errptr()) unexpected_type(std::move(temp));
4040
    std::swap(this->m_has_val, rhs.m_has_val);
4041
#endif
4042
  }
4043
4044
 public:
4045
  template <class OT = T, class OE = E>
4046
  detail::enable_if_t<detail::is_swappable<OT>::value &&
4047
                      detail::is_swappable<OE>::value &&
4048
                      (std::is_nothrow_move_constructible<OT>::value ||
4049
                       std::is_nothrow_move_constructible<OE>::value)>
4050
  swap(expected& rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
4051
                               detail::is_nothrow_swappable<T>::value &&
4052
                               std::is_nothrow_move_constructible<E>::value &&
4053
                               detail::is_nothrow_swappable<E>::value) {
4054
    if (has_value() && rhs.has_value()) {
4055
      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
4056
    } else if (!has_value() && rhs.has_value()) {
4057
      rhs.swap(*this);
4058
    } else if (has_value()) {
4059
      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
4060
    } else {
4061
      using std::swap;
4062
      swap(err(), rhs.err());
4063
    }
4064
  }
4065
4066
  constexpr const T* operator->() const {
4067
    TL_ASSERT(has_value());
4068
    return valptr();
4069
  }
4070
2.15M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4071
2.15M
    TL_ASSERT(has_value());
4072
2.15M
    return valptr();
4073
2.15M
  }
tl::expected<ada::url, ada::errors>::operator->()
Line
Count
Source
4070
863k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4071
863k
    TL_ASSERT(has_value());
4072
863k
    return valptr();
4073
863k
  }
tl::expected<ada::url_aggregator, ada::errors>::operator->()
Line
Count
Source
4070
1.28M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4071
1.28M
    TL_ASSERT(has_value());
4072
1.28M
    return valptr();
4073
1.28M
  }
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->()
4074
4075
  template <class U = T,
4076
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4077
  constexpr const U& operator*() const& {
4078
    TL_ASSERT(has_value());
4079
    return val();
4080
  }
4081
  template <class U = T,
4082
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4083
191k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
191k
    TL_ASSERT(has_value());
4085
191k
    return val();
4086
191k
  }
_ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
4083
71.0k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
71.0k
    TL_ASSERT(has_value());
4085
71.0k
    return val();
4086
71.0k
  }
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
4083
120k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
120k
    TL_ASSERT(has_value());
4085
120k
    return val();
4086
120k
  }
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
4087
  template <class U = T,
4088
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4089
  constexpr const U&& operator*() const&& {
4090
    TL_ASSERT(has_value());
4091
    return std::move(val());
4092
  }
4093
  template <class U = T,
4094
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4095
  TL_EXPECTED_11_CONSTEXPR U&& operator*() && {
4096
    TL_ASSERT(has_value());
4097
    return std::move(val());
4098
  }
4099
4100
2.43M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url, ada::errors>::has_value() const
Line
Count
Source
4100
982k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url_aggregator, ada::errors>::has_value() const
Line
Count
Source
4100
1.45M
  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
4101
402k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url, ada::errors>::operator bool() const
Line
Count
Source
4101
157k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url_aggregator, ada::errors>::operator bool() const
Line
Count
Source
4101
245k
  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
4102
4103
  template <class U = T,
4104
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4105
  TL_EXPECTED_11_CONSTEXPR const U& value() const& {
4106
    if (!has_value())
4107
      detail::throw_exception(bad_expected_access<E>(err().value()));
4108
    return val();
4109
  }
4110
  template <class U = T,
4111
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4112
0
  TL_EXPECTED_11_CONSTEXPR U& value() & {
4113
0
    if (!has_value())
4114
0
      detail::throw_exception(bad_expected_access<E>(err().value()));
4115
0
    return val();
4116
0
  }
4117
  template <class U = T,
4118
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4119
  TL_EXPECTED_11_CONSTEXPR const U&& value() const&& {
4120
    if (!has_value())
4121
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4122
    return std::move(val());
4123
  }
4124
  template <class U = T,
4125
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4126
  TL_EXPECTED_11_CONSTEXPR U&& value() && {
4127
    if (!has_value())
4128
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4129
    return std::move(val());
4130
  }
4131
4132
  constexpr const E& error() const& {
4133
    TL_ASSERT(!has_value());
4134
    return err().value();
4135
  }
4136
0
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4137
0
    TL_ASSERT(!has_value());
4138
0
    return err().value();
4139
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() &
4140
  constexpr const E&& error() const&& {
4141
    TL_ASSERT(!has_value());
4142
    return std::move(err().value());
4143
  }
4144
  TL_EXPECTED_11_CONSTEXPR E&& error() && {
4145
    TL_ASSERT(!has_value());
4146
    return std::move(err().value());
4147
  }
4148
4149
  template <class U>
4150
  constexpr T value_or(U&& v) const& {
4151
    static_assert(std::is_copy_constructible<T>::value &&
4152
                      std::is_convertible<U&&, T>::value,
4153
                  "T must be copy-constructible and convertible to from U&&");
4154
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
4155
  }
4156
  template <class U>
4157
  TL_EXPECTED_11_CONSTEXPR T value_or(U&& v) && {
4158
    static_assert(std::is_move_constructible<T>::value &&
4159
                      std::is_convertible<U&&, T>::value,
4160
                  "T must be move-constructible and convertible to from U&&");
4161
    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
4162
  }
4163
};
4164
4165
namespace detail {
4166
template <class Exp>
4167
using exp_t = typename detail::decay_t<Exp>::value_type;
4168
template <class Exp>
4169
using err_t = typename detail::decay_t<Exp>::error_type;
4170
template <class Exp, class Ret>
4171
using ret_t = expected<Ret, err_t<Exp>>;
4172
4173
#ifdef TL_EXPECTED_CXX14
4174
template <class Exp, class F,
4175
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4176
          class Ret = decltype(detail::invoke(std::declval<F>(),
4177
                                              *std::declval<Exp>()))>
4178
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4179
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4180
4181
  return exp.has_value()
4182
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4183
             : Ret(unexpect, std::forward<Exp>(exp).error());
4184
}
4185
4186
template <class Exp, class F,
4187
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4188
          class Ret = decltype(detail::invoke(std::declval<F>()))>
4189
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4190
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4191
4192
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4193
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4194
}
4195
#else
4196
template <class>
4197
struct TC;
4198
template <class Exp, class F,
4199
          class Ret = decltype(detail::invoke(std::declval<F>(),
4200
                                              *std::declval<Exp>())),
4201
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr>
4202
auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4203
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4204
4205
  return exp.has_value()
4206
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4207
             : Ret(unexpect, std::forward<Exp>(exp).error());
4208
}
4209
4210
template <class Exp, class F,
4211
          class Ret = decltype(detail::invoke(std::declval<F>())),
4212
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr>
4213
constexpr auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4214
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4215
4216
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4217
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4218
}
4219
#endif
4220
4221
#ifdef TL_EXPECTED_CXX14
4222
template <class Exp, class F,
4223
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4224
          class Ret = decltype(detail::invoke(std::declval<F>(),
4225
                                              *std::declval<Exp>())),
4226
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4227
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4228
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4229
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4230
                                                 *std::forward<Exp>(exp)))
4231
                         : result(unexpect, std::forward<Exp>(exp).error());
4232
}
4233
4234
template <class Exp, class F,
4235
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4236
          class Ret = decltype(detail::invoke(std::declval<F>(),
4237
                                              *std::declval<Exp>())),
4238
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4239
auto expected_map_impl(Exp&& exp, F&& f) {
4240
  using result = expected<void, err_t<Exp>>;
4241
  if (exp.has_value()) {
4242
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4243
    return result();
4244
  }
4245
4246
  return result(unexpect, std::forward<Exp>(exp).error());
4247
}
4248
4249
template <class Exp, class F,
4250
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4251
          class Ret = decltype(detail::invoke(std::declval<F>())),
4252
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4253
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4254
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4255
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4256
                         : result(unexpect, std::forward<Exp>(exp).error());
4257
}
4258
4259
template <class Exp, class F,
4260
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4261
          class Ret = decltype(detail::invoke(std::declval<F>())),
4262
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4263
auto expected_map_impl(Exp&& exp, F&& f) {
4264
  using result = expected<void, err_t<Exp>>;
4265
  if (exp.has_value()) {
4266
    detail::invoke(std::forward<F>(f));
4267
    return result();
4268
  }
4269
4270
  return result(unexpect, std::forward<Exp>(exp).error());
4271
}
4272
#else
4273
template <class Exp, class F,
4274
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4275
          class Ret = decltype(detail::invoke(std::declval<F>(),
4276
                                              *std::declval<Exp>())),
4277
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4278
4279
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4280
    -> ret_t<Exp, detail::decay_t<Ret>> {
4281
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4282
4283
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4284
                                                 *std::forward<Exp>(exp)))
4285
                         : result(unexpect, std::forward<Exp>(exp).error());
4286
}
4287
4288
template <class Exp, class F,
4289
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4290
          class Ret = decltype(detail::invoke(std::declval<F>(),
4291
                                              *std::declval<Exp>())),
4292
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4293
4294
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4295
  if (exp.has_value()) {
4296
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4297
    return {};
4298
  }
4299
4300
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4301
}
4302
4303
template <class Exp, class F,
4304
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4305
          class Ret = decltype(detail::invoke(std::declval<F>())),
4306
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4307
4308
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4309
    -> ret_t<Exp, detail::decay_t<Ret>> {
4310
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4311
4312
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4313
                         : result(unexpect, std::forward<Exp>(exp).error());
4314
}
4315
4316
template <class Exp, class F,
4317
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4318
          class Ret = decltype(detail::invoke(std::declval<F>())),
4319
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4320
4321
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4322
  if (exp.has_value()) {
4323
    detail::invoke(std::forward<F>(f));
4324
    return {};
4325
  }
4326
4327
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4328
}
4329
#endif
4330
4331
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4332
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4333
template <class Exp, class F,
4334
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4335
          class Ret = decltype(detail::invoke(std::declval<F>(),
4336
                                              std::declval<Exp>().error())),
4337
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4338
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4339
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4340
  return exp.has_value()
4341
             ? result(*std::forward<Exp>(exp))
4342
             : result(unexpect, detail::invoke(std::forward<F>(f),
4343
                                               std::forward<Exp>(exp).error()));
4344
}
4345
template <class Exp, class F,
4346
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4347
          class Ret = decltype(detail::invoke(std::declval<F>(),
4348
                                              std::declval<Exp>().error())),
4349
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4350
auto map_error_impl(Exp&& exp, F&& f) {
4351
  using result = expected<exp_t<Exp>, monostate>;
4352
  if (exp.has_value()) {
4353
    return result(*std::forward<Exp>(exp));
4354
  }
4355
4356
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4357
  return result(unexpect, monostate{});
4358
}
4359
template <class Exp, class F,
4360
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4361
          class Ret = decltype(detail::invoke(std::declval<F>(),
4362
                                              std::declval<Exp>().error())),
4363
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4364
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4365
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4366
  return exp.has_value()
4367
             ? result()
4368
             : result(unexpect, detail::invoke(std::forward<F>(f),
4369
                                               std::forward<Exp>(exp).error()));
4370
}
4371
template <class Exp, class F,
4372
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4373
          class Ret = decltype(detail::invoke(std::declval<F>(),
4374
                                              std::declval<Exp>().error())),
4375
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4376
auto map_error_impl(Exp&& exp, F&& f) {
4377
  using result = expected<exp_t<Exp>, monostate>;
4378
  if (exp.has_value()) {
4379
    return result();
4380
  }
4381
4382
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4383
  return result(unexpect, monostate{});
4384
}
4385
#else
4386
template <class Exp, class F,
4387
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4388
          class Ret = decltype(detail::invoke(std::declval<F>(),
4389
                                              std::declval<Exp>().error())),
4390
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4391
constexpr auto map_error_impl(Exp&& exp, F&& f)
4392
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4393
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4394
4395
  return exp.has_value()
4396
             ? result(*std::forward<Exp>(exp))
4397
             : result(unexpect, detail::invoke(std::forward<F>(f),
4398
                                               std::forward<Exp>(exp).error()));
4399
}
4400
4401
template <class Exp, class F,
4402
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4403
          class Ret = decltype(detail::invoke(std::declval<F>(),
4404
                                              std::declval<Exp>().error())),
4405
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4406
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4407
  using result = expected<exp_t<Exp>, monostate>;
4408
  if (exp.has_value()) {
4409
    return result(*std::forward<Exp>(exp));
4410
  }
4411
4412
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4413
  return result(unexpect, monostate{});
4414
}
4415
4416
template <class Exp, class F,
4417
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4418
          class Ret = decltype(detail::invoke(std::declval<F>(),
4419
                                              std::declval<Exp>().error())),
4420
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4421
constexpr auto map_error_impl(Exp&& exp, F&& f)
4422
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4423
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4424
4425
  return exp.has_value()
4426
             ? result()
4427
             : result(unexpect, detail::invoke(std::forward<F>(f),
4428
                                               std::forward<Exp>(exp).error()));
4429
}
4430
4431
template <class Exp, class F,
4432
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4433
          class Ret = decltype(detail::invoke(std::declval<F>(),
4434
                                              std::declval<Exp>().error())),
4435
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4436
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4437
  using result = expected<exp_t<Exp>, monostate>;
4438
  if (exp.has_value()) {
4439
    return result();
4440
  }
4441
4442
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4443
  return result(unexpect, monostate{});
4444
}
4445
#endif
4446
4447
#ifdef TL_EXPECTED_CXX14
4448
template <class Exp, class F,
4449
          class Ret = decltype(detail::invoke(std::declval<F>(),
4450
                                              std::declval<Exp>().error())),
4451
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4452
constexpr auto or_else_impl(Exp&& exp, F&& f) {
4453
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4454
  return exp.has_value() ? std::forward<Exp>(exp)
4455
                         : detail::invoke(std::forward<F>(f),
4456
                                          std::forward<Exp>(exp).error());
4457
}
4458
4459
template <class Exp, class F,
4460
          class Ret = decltype(detail::invoke(std::declval<F>(),
4461
                                              std::declval<Exp>().error())),
4462
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4463
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4464
  return exp.has_value() ? std::forward<Exp>(exp)
4465
                         : (detail::invoke(std::forward<F>(f),
4466
                                           std::forward<Exp>(exp).error()),
4467
                            std::forward<Exp>(exp));
4468
}
4469
#else
4470
template <class Exp, class F,
4471
          class Ret = decltype(detail::invoke(std::declval<F>(),
4472
                                              std::declval<Exp>().error())),
4473
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4474
auto or_else_impl(Exp&& exp, F&& f) -> Ret {
4475
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4476
  return exp.has_value() ? std::forward<Exp>(exp)
4477
                         : detail::invoke(std::forward<F>(f),
4478
                                          std::forward<Exp>(exp).error());
4479
}
4480
4481
template <class Exp, class F,
4482
          class Ret = decltype(detail::invoke(std::declval<F>(),
4483
                                              std::declval<Exp>().error())),
4484
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4485
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4486
  return exp.has_value() ? std::forward<Exp>(exp)
4487
                         : (detail::invoke(std::forward<F>(f),
4488
                                           std::forward<Exp>(exp).error()),
4489
                            std::forward<Exp>(exp));
4490
}
4491
#endif
4492
}  // namespace detail
4493
4494
template <class T, class E, class U, class F>
4495
constexpr bool operator==(const expected<T, E>& lhs,
4496
                          const expected<U, F>& rhs) {
4497
  return (lhs.has_value() != rhs.has_value())
4498
             ? false
4499
             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
4500
}
4501
template <class T, class E, class U, class F>
4502
constexpr bool operator!=(const expected<T, E>& lhs,
4503
                          const expected<U, F>& rhs) {
4504
  return (lhs.has_value() != rhs.has_value())
4505
             ? true
4506
             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4507
}
4508
template <class E, class F>
4509
constexpr bool operator==(const expected<void, E>& lhs,
4510
                          const expected<void, F>& rhs) {
4511
  return (lhs.has_value() != rhs.has_value())
4512
             ? false
4513
             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
4514
}
4515
template <class E, class F>
4516
constexpr bool operator!=(const expected<void, E>& lhs,
4517
                          const expected<void, F>& rhs) {
4518
  return (lhs.has_value() != rhs.has_value())
4519
             ? true
4520
             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4521
}
4522
4523
template <class T, class E, class U>
4524
constexpr bool operator==(const expected<T, E>& x, const U& v) {
4525
  return x.has_value() ? *x == v : false;
4526
}
4527
template <class T, class E, class U>
4528
constexpr bool operator==(const U& v, const expected<T, E>& x) {
4529
  return x.has_value() ? *x == v : false;
4530
}
4531
template <class T, class E, class U>
4532
constexpr bool operator!=(const expected<T, E>& x, const U& v) {
4533
  return x.has_value() ? *x != v : true;
4534
}
4535
template <class T, class E, class U>
4536
constexpr bool operator!=(const U& v, const expected<T, E>& x) {
4537
  return x.has_value() ? *x != v : true;
4538
}
4539
4540
template <class T, class E>
4541
constexpr bool operator==(const expected<T, E>& x, const unexpected<E>& e) {
4542
  return x.has_value() ? false : x.error() == e.value();
4543
}
4544
template <class T, class E>
4545
constexpr bool operator==(const unexpected<E>& e, const expected<T, E>& x) {
4546
  return x.has_value() ? false : x.error() == e.value();
4547
}
4548
template <class T, class E>
4549
constexpr bool operator!=(const expected<T, E>& x, const unexpected<E>& e) {
4550
  return x.has_value() ? true : x.error() != e.value();
4551
}
4552
template <class T, class E>
4553
constexpr bool operator!=(const unexpected<E>& e, const expected<T, E>& x) {
4554
  return x.has_value() ? true : x.error() != e.value();
4555
}
4556
4557
template <class T, class E,
4558
          detail::enable_if_t<(std::is_void<T>::value ||
4559
                               std::is_move_constructible<T>::value) &&
4560
                              detail::is_swappable<T>::value &&
4561
                              std::is_move_constructible<E>::value &&
4562
                              detail::is_swappable<E>::value>* = nullptr>
4563
void swap(expected<T, E>& lhs,
4564
          expected<T, E>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
4565
  lhs.swap(rhs);
4566
}
4567
}  // namespace tl
4568
4569
#endif
4570
/* end file include/ada/expected.h */
4571
4572
/* begin file include/ada/url_pattern_regex.h */
4573
/**
4574
 * @file url_search_params.h
4575
 * @brief Declaration for the URL Search Params
4576
 */
4577
#ifndef ADA_URL_PATTERN_REGEX_H
4578
#define ADA_URL_PATTERN_REGEX_H
4579
4580
#include <concepts>
4581
#include <optional>
4582
#include <string>
4583
#include <string_view>
4584
#include <vector>
4585
4586
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4587
#include <regex>
4588
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4589
4590
#if ADA_INCLUDE_URL_PATTERN
4591
namespace ada::url_pattern_regex {
4592
4593
/**
4594
 * @brief Concept defining the interface for regex engine providers used by
4595
 * URLPattern.
4596
 *
4597
 * A regex provider supplies the regular expression engine used for
4598
 * pattern compilation and matching.
4599
 *
4600
 * Requirements:
4601
 * - typename T::regex_type: The compiled regex object type.
4602
 * - T::create_instance(pattern, ignore_case): Compiles the regex pattern
4603
 * string. Returns std::nullopt if the pattern is invalid or cannot be compiled.
4604
 * - T::regex_search(input, pattern): Executes a regex search against the input,
4605
 *     returning a vector of capture group values (with std::nullopt for
4606
 * unmatched optional groups) or std::nullopt if the input does not match.
4607
 * - T::regex_match(input, pattern): Returns true if the input matches the
4608
 * pattern, or false otherwise.
4609
 *
4610
 * Exception contract:
4611
 * Provider methods may throw exceptions (for example, on allocation failure,
4612
 * input length limits, complexity limits, or internal engine errors). In
4613
 * environments where C++ exceptions are enabled, exceptions thrown by the
4614
 * provider will propagate out of URLPattern functions (such as fast_test,
4615
 * fast_match, test, exec, match, etc.). Providers that prefer non-throwing
4616
 * operation should handle internal errors and return false or std::nullopt.
4617
 */
4618
template <typename T>
4619
concept regex_concept = requires(T t, std::string_view pattern,
4620
                                 bool ignore_case, std::string_view input) {
4621
  // Ensure the class has a type alias 'regex_type'
4622
  typename T::regex_type;
4623
4624
  // Function to create a regex instance
4625
  {
4626
    T::create_instance(pattern, ignore_case)
4627
  } -> std::same_as<std::optional<typename T::regex_type>>;
4628
4629
  // Function to perform regex search
4630
  {
4631
    T::regex_search(input, std::declval<typename T::regex_type&>())
4632
  } -> std::same_as<std::optional<std::vector<std::optional<std::string>>>>;
4633
4634
  // Function to match regex pattern
4635
  {
4636
    T::regex_match(input, std::declval<typename T::regex_type&>())
4637
  } -> std::same_as<bool>;
4638
4639
  // Copy constructor
4640
  { T(std::declval<const T&>()) } -> std::same_as<T>;
4641
4642
  // Move constructor
4643
  { T(std::declval<T&&>()) } -> std::same_as<T>;
4644
};
4645
4646
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4647
class std_regex_provider final {
4648
 public:
4649
  std_regex_provider() = default;
4650
  using regex_type = std::regex;
4651
  static std::optional<regex_type> create_instance(std::string_view pattern,
4652
                                                   bool ignore_case);
4653
  static std::optional<std::vector<std::optional<std::string>>> regex_search(
4654
      std::string_view input, const regex_type& pattern);
4655
  static bool regex_match(std::string_view input, const regex_type& pattern);
4656
};
4657
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4658
4659
}  // namespace ada::url_pattern_regex
4660
#endif  // ADA_INCLUDE_URL_PATTERN
4661
#endif  // ADA_URL_PATTERN_REGEX_H
4662
/* end file include/ada/url_pattern_regex.h */
4663
/* begin file include/ada/url_pattern_init.h */
4664
/**
4665
 * @file url_pattern_init.h
4666
 * @brief Declaration for the url_pattern_init implementation.
4667
 */
4668
#ifndef ADA_URL_PATTERN_INIT_H
4669
#define ADA_URL_PATTERN_INIT_H
4670
4671
/* begin file include/ada/errors.h */
4672
/**
4673
 * @file errors.h
4674
 * @brief Error type definitions for URL parsing.
4675
 *
4676
 * Defines the error codes that can be returned when URL parsing fails.
4677
 */
4678
#ifndef ADA_ERRORS_H
4679
#define ADA_ERRORS_H
4680
4681
#include <cstdint>
4682
namespace ada {
4683
/**
4684
 * @brief Error codes for URL parsing operations.
4685
 *
4686
 * Used with `tl::expected` to indicate why a URL parsing operation failed.
4687
 */
4688
enum class errors : uint8_t {
4689
  type_error /**< A type error occurred (e.g., invalid URL syntax). */
4690
};
4691
}  // namespace ada
4692
#endif  // ADA_ERRORS_H
4693
/* end file include/ada/errors.h */
4694
4695
#include <concepts>
4696
#include <cstdint>
4697
#include <iostream>
4698
#include <optional>
4699
#include <string>
4700
#include <string_view>
4701
4702
#if ADA_TESTING
4703
#include <iostream>
4704
#endif  // ADA_TESTING
4705
4706
#if ADA_INCLUDE_URL_PATTERN
4707
namespace ada {
4708
4709
// Important: C++20 allows us to use concept rather than `using` or `typedef
4710
// and allows functions with second argument, which is optional (using either
4711
// std::nullopt or a parameter with default value)
4712
template <typename F>
4713
concept url_pattern_encoding_callback = requires(F f, std::string_view sv) {
4714
  { f(sv) } -> std::same_as<tl::expected<std::string, errors>>;
4715
};
4716
4717
// A structure providing matching patterns for individual components
4718
// of a URL. When a URLPattern is created, or when a URLPattern is
4719
// used to match or test against a URL, the input can be given as
4720
// either a string or a URLPatternInit struct. If a string is given,
4721
// it will be parsed to create a URLPatternInit. The URLPatternInit
4722
// API is defined as part of the URLPattern specification.
4723
// All provided strings must be valid UTF-8.
4724
struct url_pattern_init {
4725
  enum class process_type : uint8_t {
4726
    url,
4727
    pattern,
4728
  };
4729
4730
0
  friend std::ostream& operator<<(std::ostream& os, process_type type) {
4731
0
    switch (type) {
4732
0
      case process_type::url:
4733
0
        return os << "url";
4734
0
      case process_type::pattern:
4735
0
        return os << "pattern";
4736
0
      default:
4737
0
        return os << "unknown";
4738
0
    }
4739
0
  }
4740
4741
  // All strings must be valid UTF-8.
4742
  // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
4743
  static tl::expected<url_pattern_init, errors> process(
4744
      const url_pattern_init& init, process_type type,
4745
      std::optional<std::string_view> protocol = std::nullopt,
4746
      std::optional<std::string_view> username = std::nullopt,
4747
      std::optional<std::string_view> password = std::nullopt,
4748
      std::optional<std::string_view> hostname = std::nullopt,
4749
      std::optional<std::string_view> port = std::nullopt,
4750
      std::optional<std::string_view> pathname = std::nullopt,
4751
      std::optional<std::string_view> search = std::nullopt,
4752
      std::optional<std::string_view> hash = std::nullopt);
4753
4754
  // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init
4755
  static tl::expected<std::string, errors> process_protocol(
4756
      std::string_view value, process_type type);
4757
4758
  // @see https://urlpattern.spec.whatwg.org/#process-username-for-init
4759
  static tl::expected<std::string, errors> process_username(
4760
      std::string_view value, process_type type);
4761
4762
  // @see https://urlpattern.spec.whatwg.org/#process-password-for-init
4763
  static tl::expected<std::string, errors> process_password(
4764
      std::string_view value, process_type type);
4765
4766
  // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init
4767
  static tl::expected<std::string, errors> process_hostname(
4768
      std::string_view value, process_type type);
4769
4770
  // @see https://urlpattern.spec.whatwg.org/#process-port-for-init
4771
  static tl::expected<std::string, errors> process_port(
4772
      std::string_view port, std::string_view protocol, process_type type);
4773
4774
  // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init
4775
  static tl::expected<std::string, errors> process_pathname(
4776
      std::string_view value, std::string_view protocol, process_type type);
4777
4778
  // @see https://urlpattern.spec.whatwg.org/#process-search-for-init
4779
  static tl::expected<std::string, errors> process_search(
4780
      std::string_view value, process_type type);
4781
4782
  // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init
4783
  static tl::expected<std::string, errors> process_hash(std::string_view value,
4784
                                                        process_type type);
4785
4786
#if ADA_TESTING
4787
  friend void PrintTo(const url_pattern_init& init, std::ostream* os) {
4788
    *os << "protocol: '" << init.protocol.value_or("undefined") << "', ";
4789
    *os << "username: '" << init.username.value_or("undefined") << "', ";
4790
    *os << "password: '" << init.password.value_or("undefined") << "', ";
4791
    *os << "hostname: '" << init.hostname.value_or("undefined") << "', ";
4792
    *os << "port: '" << init.port.value_or("undefined") << "', ";
4793
    *os << "pathname: '" << init.pathname.value_or("undefined") << "', ";
4794
    *os << "search: '" << init.search.value_or("undefined") << "', ";
4795
    *os << "hash: '" << init.hash.value_or("undefined") << "', ";
4796
    *os << "base_url: '" << init.base_url.value_or("undefined") << "', ";
4797
  }
4798
#endif  // ADA_TESTING
4799
4800
  bool operator==(const url_pattern_init&) const;
4801
  // If present, must be valid UTF-8.
4802
  std::optional<std::string> protocol{};
4803
  // If present, must be valid UTF-8.
4804
  std::optional<std::string> username{};
4805
  // If present, must be valid UTF-8.
4806
  std::optional<std::string> password{};
4807
  // If present, must be valid UTF-8.
4808
  std::optional<std::string> hostname{};
4809
  // If present, must be valid UTF-8.
4810
  std::optional<std::string> port{};
4811
  // If present, must be valid UTF-8.
4812
  std::optional<std::string> pathname{};
4813
  // If present, must be valid UTF-8.
4814
  std::optional<std::string> search{};
4815
  // If present, must be valid UTF-8.
4816
  std::optional<std::string> hash{};
4817
  // If present, must be valid UTF-8.
4818
  std::optional<std::string> base_url{};
4819
};
4820
}  // namespace ada
4821
#endif  // ADA_INCLUDE_URL_PATTERN
4822
#endif  // ADA_URL_PATTERN_INIT_H
4823
/* end file include/ada/url_pattern_init.h */
4824
4825
/** @private Forward declarations */
4826
namespace ada {
4827
struct url_aggregator;
4828
struct url;
4829
#if ADA_INCLUDE_URL_PATTERN
4830
template <url_pattern_regex::regex_concept regex_provider>
4831
class url_pattern;
4832
struct url_pattern_options;
4833
#endif  // ADA_INCLUDE_URL_PATTERN
4834
enum class errors : uint8_t;
4835
}  // namespace ada
4836
4837
/**
4838
 * @namespace ada::parser
4839
 * @brief Internal URL parsing implementation.
4840
 *
4841
 * Contains the core URL parsing algorithm as specified by the WHATWG URL
4842
 * Standard. These functions are used internally by `ada::parse()`.
4843
 */
4844
namespace ada::parser {
4845
/**
4846
 * Parses a URL string into a URL object.
4847
 *
4848
 * @tparam result_type The type of URL object to create (url or url_aggregator).
4849
 *
4850
 * @param user_input The URL string to parse (must be valid UTF-8).
4851
 * @param base_url Optional base URL for resolving relative URLs.
4852
 *
4853
 * @return The parsed URL object. Check `is_valid` to determine if parsing
4854
 *         succeeded.
4855
 *
4856
 * @see https://url.spec.whatwg.org/#concept-basic-url-parser
4857
 */
4858
template <typename result_type = url_aggregator>
4859
result_type parse_url(std::string_view user_input,
4860
                      const result_type* base_url = nullptr);
4861
4862
extern template url_aggregator parse_url<url_aggregator>(
4863
    std::string_view user_input, const url_aggregator* base_url);
4864
extern template url parse_url<url>(std::string_view user_input,
4865
                                   const url* base_url);
4866
4867
template <typename result_type = url_aggregator, bool store_values = true>
4868
result_type parse_url_impl(std::string_view user_input,
4869
                           const result_type* base_url = nullptr);
4870
4871
extern template url_aggregator parse_url_impl<url_aggregator, true>(
4872
    std::string_view user_input, const url_aggregator* base_url);
4873
extern template url_aggregator parse_url_impl<url_aggregator, false>(
4874
    std::string_view user_input, const url_aggregator* base_url);
4875
extern template url parse_url_impl<url, true>(std::string_view user_input,
4876
                                              const url* base_url);
4877
4878
/** @private */
4879
template <class result_type>
4880
bool try_parse_simple_absolute(std::string_view input, result_type& out);
4881
4882
/** @private */
4883
template <class result_type>
4884
bool finish_simple_absolute_with_port(std::string_view input, result_type& out,
4885
                                      ada::scheme::type scheme_type,
4886
                                      uint32_t protocol_end, size_t host_start,
4887
                                      size_t host_end, size_t host_len,
4888
                                      bool has_upper);
4889
4890
/** @private */
4891
template <class result_type>
4892
bool try_parse_simple_relative(std::string_view input, const result_type& base,
4893
                               result_type& out);
4894
4895
#if ADA_INCLUDE_URL_PATTERN
4896
template <url_pattern_regex::regex_concept regex_provider>
4897
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
4898
    std::variant<std::string_view, url_pattern_init>&& input,
4899
    const std::string_view* base_url, const url_pattern_options* options);
4900
#endif  // ADA_INCLUDE_URL_PATTERN
4901
4902
}  // namespace ada::parser
4903
4904
#endif  // ADA_PARSER_H
4905
/* end file include/ada/parser.h */
4906
/* begin file include/ada/parser-inl.h */
4907
/**
4908
 * @file parser-inl.h
4909
 */
4910
#ifndef ADA_PARSER_INL_H
4911
#define ADA_PARSER_INL_H
4912
4913
/* begin file include/ada/url_pattern.h */
4914
/**
4915
 * @file url_pattern.h
4916
 * @brief URLPattern API implementation.
4917
 *
4918
 * This header provides the URLPattern API as specified by the WHATWG URL
4919
 * Pattern Standard. URLPattern allows matching URLs against patterns with
4920
 * wildcards and named groups, similar to how regular expressions match strings.
4921
 *
4922
 * @see https://urlpattern.spec.whatwg.org/
4923
 * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
4924
 */
4925
#ifndef ADA_URL_PATTERN_H
4926
#define ADA_URL_PATTERN_H
4927
4928
/* begin file include/ada/implementation.h */
4929
/**
4930
 * @file implementation.h
4931
 * @brief User-facing functions for URL parsing and manipulation.
4932
 *
4933
 * This header provides the primary public API for parsing URLs in Ada.
4934
 * It includes the main `ada::parse()` function which is the recommended
4935
 * entry point for most users.
4936
 *
4937
 * @see https://url.spec.whatwg.org/#api
4938
 */
4939
#ifndef ADA_IMPLEMENTATION_H
4940
#define ADA_IMPLEMENTATION_H
4941
4942
#include <cstdint>
4943
#include <optional>
4944
#include <span>
4945
#include <string>
4946
#include <string_view>
4947
#include <variant>
4948
4949
/* begin file include/ada/url.h */
4950
/**
4951
 * @file url.h
4952
 * @brief Declaration for the `ada::url` class.
4953
 *
4954
 * This file contains the `ada::url` struct which represents a parsed URL
4955
 * using separate `std::string` instances for each component. This
4956
 * representation is more flexible but uses more memory than `url_aggregator`.
4957
 *
4958
 * @see url_aggregator.h for a more memory-efficient alternative
4959
 */
4960
#ifndef ADA_URL_H
4961
#define ADA_URL_H
4962
4963
#include <algorithm>
4964
#include <cstddef>
4965
#include <cstdint>
4966
#include <optional>
4967
#include <ostream>
4968
#include <string>
4969
#include <string_view>
4970
4971
/* begin file include/ada/url_components.h */
4972
/**
4973
 * @file url_components.h
4974
 * @brief URL component offset representation for url_aggregator.
4975
 *
4976
 * This file defines the `url_components` struct which stores byte offsets
4977
 * into a URL string buffer. It is used internally by `url_aggregator` to
4978
 * efficiently locate URL components without storing separate strings.
4979
 */
4980
#ifndef ADA_URL_COMPONENTS_H
4981
#define ADA_URL_COMPONENTS_H
4982
4983
#include <cstdint>
4984
#include <string>
4985
4986
namespace ada {
4987
4988
/**
4989
 * @brief Stores byte offsets for URL components within a buffer.
4990
 *
4991
 * The `url_components` struct uses 32-bit offsets to track the boundaries
4992
 * of each URL component within a single string buffer. This enables efficient
4993
 * component extraction without additional memory allocations.
4994
 *
4995
 * Component layout in a URL:
4996
 * ```
4997
 * https://user:pass@example.com:1234/foo/bar?baz#quux
4998
 *       |     |    |          | ^^^^|       |   |
4999
 *       |     |    |          | |   |       |   `----- hash_start
5000
 *       |     |    |          | |   |       `--------- search_start
5001
 *       |     |    |          | |   `----------------- pathname_start
5002
 *       |     |    |          | `--------------------- port
5003
 *       |     |    |          `----------------------- host_end
5004
 *       |     |    `---------------------------------- host_start
5005
 *       |     `--------------------------------------- username_end
5006
 *       `--------------------------------------------- protocol_end
5007
 * ```
5008
 *
5009
 * @note The 32-bit offsets limit URLs to 4GB in length.
5010
 * @note A value of `omitted` (UINT32_MAX) indicates the component is not
5011
 * present.
5012
 */
5013
struct url_components {
5014
  /** Sentinel value indicating a component is not present. */
5015
  constexpr static uint32_t omitted = uint32_t(-1);
5016
5017
248k
  url_components() = default;
5018
  url_components(const url_components& u) = default;
5019
  url_components(url_components&& u) noexcept = default;
5020
  url_components& operator=(url_components&& u) noexcept = default;
5021
  url_components& operator=(const url_components& u) = default;
5022
  ~url_components() = default;
5023
5024
  /** Offset of the end of the protocol/scheme (position of ':'). */
5025
  uint32_t protocol_end{0};
5026
5027
  /**
5028
   * Offset of the end of the username.
5029
   * Initialized to 0 (not `omitted`) to simplify username/password getters.
5030
   */
5031
  uint32_t username_end{0};
5032
5033
  /** Offset of the start of the host. */
5034
  uint32_t host_start{0};
5035
5036
  /** Offset of the end of the host. */
5037
  uint32_t host_end{0};
5038
5039
  /** Port number, or `omitted` if no port is specified. */
5040
  uint32_t port{omitted};
5041
5042
  /** Offset of the start of the pathname. */
5043
  uint32_t pathname_start{0};
5044
5045
  /** Offset of the '?' starting the query, or `omitted` if no query. */
5046
  uint32_t search_start{omitted};
5047
5048
  /** Offset of the '#' starting the fragment, or `omitted` if no fragment. */
5049
  uint32_t hash_start{omitted};
5050
5051
  /**
5052
   * Validates that offsets are in ascending order and consistent.
5053
   * Useful for debugging to detect internal corruption.
5054
   * @return `true` if offsets are consistent, `false` otherwise.
5055
   */
5056
  [[nodiscard]] constexpr bool check_offset_consistency() const noexcept;
5057
5058
  /**
5059
   * Returns a JSON string representation of the offsets for debugging.
5060
   * @return A JSON-formatted string with all offset values.
5061
   */
5062
  [[nodiscard]] std::string to_string() const;
5063
5064
};  // struct url_components
5065
}  // namespace ada
5066
#endif
5067
/* end file include/ada/url_components.h */
5068
5069
namespace ada {
5070
5071
struct url_aggregator;
5072
5073
// namespace parser {
5074
// template <typename result_type>
5075
// result_type parse_url(std::string_view user_input,
5076
//                       const result_type* base_url = nullptr);
5077
// template <typename result_type, bool store_values>
5078
// result_type parse_url_impl(std::string_view user_input,
5079
//                            const result_type* base_url = nullptr);
5080
// }
5081
5082
/**
5083
 * @brief Represents a parsed URL with individual string components.
5084
 *
5085
 * The `url` struct stores each URL component (scheme, username, password,
5086
 * host, port, path, query, fragment) as a separate `std::string`. This
5087
 * provides flexibility but incurs more memory allocations compared to
5088
 * `url_aggregator`.
5089
 *
5090
 * **When to use `ada::url`:**
5091
 * - When you need to frequently modify individual URL components
5092
 * - When you want independent ownership of component strings
5093
 *
5094
 * **When to use `ada::url_aggregator` instead:**
5095
 * - For read-mostly operations on parsed URLs
5096
 * - When memory efficiency is important
5097
 * - When you only need string_view access to components
5098
 *
5099
 * @note This type is returned when parsing with `ada::parse<ada::url>()`.
5100
 *       By default, `ada::parse()` returns `ada::url_aggregator`.
5101
 *
5102
 * @see url_aggregator For a more memory-efficient URL representation
5103
 * @see https://url.spec.whatwg.org/#url-representation
5104
 */
5105
struct url : url_base {
5106
129k
  url() = default;
5107
54.8k
  url(const url& u) = default;
5108
98.8k
  url(url&& u) noexcept = default;
5109
16.7k
  url& operator=(url&& u) noexcept = default;
5110
7.89k
  url& operator=(const url& u) = default;
5111
283k
  ~url() override = default;
5112
5113
  // Fields are ordered so that the most frequently accessed components
5114
  // tend to occupy earlier cache lines and remain close together in memory.
5115
  //
5116
  // Note: The exact object layout (including cache-line boundaries, byte
5117
  // offsets, and member sizes) is implementation- and platform-dependent.
5118
  // This ordering expresses an intent for better cache locality but does not
5119
  // guarantee any specific in-memory layout.
5120
5121
  /**
5122
   * @private
5123
   * A URL's host is null or a host. It is initially null.
5124
   */
5125
  std::optional<std::string> host{};
5126
5127
  /**
5128
   * @private
5129
   * A URL's path is either an ASCII string or a list of zero or more ASCII
5130
   * strings, usually identifying a location.
5131
   */
5132
  std::string path{};
5133
5134
  /**
5135
   * @private
5136
   * A URL's query is either null or an ASCII string. It is initially null.
5137
   */
5138
  std::optional<std::string> query{};
5139
5140
  /**
5141
   * @private
5142
   * A URL's fragment is either null or an ASCII string that can be used for
5143
   * further processing on the resource the URL's other components identify. It
5144
   * is initially null.
5145
   */
5146
  std::optional<std::string> hash{};
5147
5148
  /**
5149
   * @private
5150
   * A URL's port is either null or a 16-bit unsigned integer that identifies a
5151
   * networking port. It is initially null.
5152
   */
5153
  std::optional<uint16_t> port{};
5154
5155
  /**
5156
   * @private
5157
   * A URL's username is an ASCII string identifying a username. It is initially
5158
   * the empty string.
5159
   */
5160
  std::string username{};
5161
5162
  /**
5163
   * @private
5164
   * A URL's password is an ASCII string identifying a password. It is initially
5165
   * the empty string.
5166
   */
5167
  std::string password{};
5168
5169
  /**
5170
   * Checks if the URL has an empty hostname (host is set but empty string).
5171
   * @return `true` if host exists but is empty, `false` otherwise.
5172
   */
5173
  [[nodiscard]] inline bool has_empty_hostname() const noexcept;
5174
5175
  /**
5176
   * Checks if the URL has a non-default port explicitly specified.
5177
   * @return `true` if a port is present, `false` otherwise.
5178
   */
5179
  [[nodiscard]] inline bool has_port() const noexcept;
5180
5181
  /**
5182
   * Checks if the URL has a hostname (including empty hostnames).
5183
   * @return `true` if host is present, `false` otherwise.
5184
   */
5185
  [[nodiscard]] inline bool has_hostname() const noexcept;
5186
5187
  /**
5188
   * Validates whether the hostname is a valid domain according to RFC 1034.
5189
   * Checks that the domain and its labels have valid lengths (max 255 octets
5190
   * total, max 63 octets per label).
5191
   * @return `true` if the domain is valid, `false` otherwise.
5192
   */
5193
  [[nodiscard]] bool has_valid_domain() const noexcept override;
5194
5195
  /**
5196
   * Returns a JSON string representation of this URL for debugging.
5197
   * @return A JSON-formatted string with all URL components.
5198
   */
5199
  [[nodiscard]] std::string to_string() const override;
5200
5201
  /**
5202
   * Returns the full serialized URL (the href).
5203
   * @return The complete URL string (allocates a new string).
5204
   * @see https://url.spec.whatwg.org/#dom-url-href
5205
   */
5206
  [[nodiscard]] ada_really_inline std::string get_href() const;
5207
5208
  /**
5209
   * Returns the byte length of the serialized URL without allocating a string.
5210
   * @return Size of the href in bytes.
5211
   */
5212
  [[nodiscard]] size_t get_href_size() const noexcept;
5213
5214
  /**
5215
   * Returns the URL's origin as a string (scheme + host + port for special
5216
   * URLs).
5217
   * @return A newly allocated string containing the serialized origin.
5218
   * @see https://url.spec.whatwg.org/#concept-url-origin
5219
   */
5220
  [[nodiscard]] std::string get_origin() const override;
5221
5222
  /**
5223
   * Returns the URL's scheme followed by a colon (e.g., "https:").
5224
   * @return A newly allocated string with the protocol.
5225
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5226
   */
5227
  [[nodiscard]] std::string get_protocol() const;
5228
5229
  /**
5230
   * Returns the URL's host and port (e.g., "example.com:8080").
5231
   * If no port is set, returns just the host. Returns empty string if no host.
5232
   * @return A newly allocated string with host:port.
5233
   * @see https://url.spec.whatwg.org/#dom-url-host
5234
   */
5235
  [[nodiscard]] std::string get_host() const;
5236
5237
  /**
5238
   * Returns the URL's hostname (without port).
5239
   * Returns empty string if no host is set.
5240
   * @return A newly allocated string with the hostname.
5241
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5242
   */
5243
  [[nodiscard]] std::string get_hostname() const;
5244
5245
  /**
5246
   * Returns the URL's path component.
5247
   * @return A string_view pointing to the path.
5248
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5249
   */
5250
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept;
5251
5252
  /**
5253
   * Returns the byte length of the pathname without creating a string.
5254
   * @return Size of the pathname in bytes.
5255
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5256
   */
5257
  [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
5258
5259
  /**
5260
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
5261
   * Returns empty string if no query is set.
5262
   * @return A newly allocated string with the search/query.
5263
   * @see https://url.spec.whatwg.org/#dom-url-search
5264
   */
5265
  [[nodiscard]] std::string get_search() const;
5266
5267
  /**
5268
   * Returns the URL's username component.
5269
   * @return A constant reference to the username string.
5270
   * @see https://url.spec.whatwg.org/#dom-url-username
5271
   */
5272
  [[nodiscard]] const std::string& get_username() const noexcept;
5273
5274
  /**
5275
   * Sets the URL's username, percent-encoding special characters.
5276
   * @param input The new username value.
5277
   * @return `true` on success, `false` if the URL cannot have credentials.
5278
   * @see https://url.spec.whatwg.org/#dom-url-username
5279
   */
5280
  bool set_username(std::string_view input);
5281
5282
  /**
5283
   * Sets the URL's password, percent-encoding special characters.
5284
   * @param input The new password value.
5285
   * @return `true` on success, `false` if the URL cannot have credentials.
5286
   * @see https://url.spec.whatwg.org/#dom-url-password
5287
   */
5288
  bool set_password(std::string_view input);
5289
5290
  /**
5291
   * Sets the URL's port from a string (e.g., "8080").
5292
   * @param input The port string. Empty string removes the port.
5293
   * @return `true` on success, `false` if the URL cannot have a port.
5294
   * @see https://url.spec.whatwg.org/#dom-url-port
5295
   */
5296
  bool set_port(std::string_view input);
5297
5298
  /**
5299
   * Sets the URL's fragment/hash (the part after '#').
5300
   * @param input The new hash value (with or without leading '#').
5301
   * @see https://url.spec.whatwg.org/#dom-url-hash
5302
   */
5303
  void set_hash(std::string_view input);
5304
5305
  /**
5306
   * Sets the URL's query string (the part after '?').
5307
   * @param input The new query value (with or without leading '?').
5308
   * @see https://url.spec.whatwg.org/#dom-url-search
5309
   */
5310
  void set_search(std::string_view input);
5311
5312
  /**
5313
   * Sets the URL's pathname.
5314
   * @param input The new path value.
5315
   * @return `true` on success, `false` if the URL has an opaque path.
5316
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5317
   */
5318
  bool set_pathname(std::string_view input);
5319
5320
  /**
5321
   * Sets the URL's host (hostname and optionally port).
5322
   * @param input The new host value (e.g., "example.com:8080").
5323
   * @return `true` on success, `false` if parsing fails.
5324
   * @see https://url.spec.whatwg.org/#dom-url-host
5325
   */
5326
  bool set_host(std::string_view input);
5327
5328
  /**
5329
   * Sets the URL's hostname (without port).
5330
   * @param input The new hostname value.
5331
   * @return `true` on success, `false` if parsing fails.
5332
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5333
   */
5334
  bool set_hostname(std::string_view input);
5335
5336
  /**
5337
   * Sets the URL's protocol/scheme.
5338
   * @param input The new protocol (with or without trailing ':').
5339
   * @return `true` on success, `false` if the scheme is invalid.
5340
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5341
   */
5342
  bool set_protocol(std::string_view input);
5343
5344
  /**
5345
   * Replaces the entire URL by parsing a new href string.
5346
   * @param input The new URL string to parse.
5347
   * @return `true` on success, `false` if parsing fails.
5348
   * @see https://url.spec.whatwg.org/#dom-url-href
5349
   */
5350
  bool set_href(std::string_view input);
5351
5352
  /**
5353
   * Returns the URL's password component.
5354
   * @return A constant reference to the password string.
5355
   * @see https://url.spec.whatwg.org/#dom-url-password
5356
   */
5357
  [[nodiscard]] const std::string& get_password() const noexcept;
5358
5359
  /**
5360
   * Returns the URL's port as a string (e.g., "8080").
5361
   * Returns empty string if no port is set.
5362
   * @return A newly allocated string with the port.
5363
   * @see https://url.spec.whatwg.org/#dom-url-port
5364
   */
5365
  [[nodiscard]] std::string get_port() const;
5366
5367
  /**
5368
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
5369
   * Returns empty string if no fragment is set.
5370
   * @return A newly allocated string with the hash.
5371
   * @see https://url.spec.whatwg.org/#dom-url-hash
5372
   */
5373
  [[nodiscard]] std::string get_hash() const;
5374
5375
  /**
5376
   * Checks if the URL has credentials (non-empty username or password).
5377
   * @return `true` if username or password is non-empty, `false` otherwise.
5378
   */
5379
  [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5380
5381
  /**
5382
   * Returns the URL component offsets for efficient serialization.
5383
   *
5384
   * The components represent byte offsets into the serialized URL:
5385
   * ```
5386
   * https://user:pass@example.com:1234/foo/bar?baz#quux
5387
   *       |     |    |          | ^^^^|       |   |
5388
   *       |     |    |          | |   |       |   `----- hash_start
5389
   *       |     |    |          | |   |       `--------- search_start
5390
   *       |     |    |          | |   `----------------- pathname_start
5391
   *       |     |    |          | `--------------------- port
5392
   *       |     |    |          `----------------------- host_end
5393
   *       |     |    `---------------------------------- host_start
5394
   *       |     `--------------------------------------- username_end
5395
   *       `--------------------------------------------- protocol_end
5396
   * ```
5397
   * @return A newly constructed url_components struct.
5398
   * @see https://github.com/servo/rust-url
5399
   */
5400
  [[nodiscard]] ada_really_inline ada::url_components get_components() const;
5401
5402
  /**
5403
   * Checks if the URL has a fragment/hash component.
5404
   * @return `true` if hash is present, `false` otherwise.
5405
   */
5406
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
5407
5408
  /**
5409
   * Checks if the URL has a query/search component.
5410
   * @return `true` if query is present, `false` otherwise.
5411
   */
5412
  [[nodiscard]] constexpr bool has_search() const noexcept override;
5413
5414
 private:
5415
  friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5416
                                                   const ada::url*);
5417
  friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5418
      std::string_view, const ada::url_aggregator*);
5419
  friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5420
      ada::url& url);
5421
5422
  friend ada::url ada::parser::parse_url_impl<ada::url, true>(std::string_view,
5423
                                                              const ada::url*);
5424
  friend ada::url_aggregator ada::parser::parse_url_impl<
5425
      ada::url_aggregator, true>(std::string_view, const ada::url_aggregator*);
5426
  friend ada::url_aggregator ada::parser::parse_url_impl<
5427
      ada::url_aggregator, false>(std::string_view, const ada::url_aggregator*);
5428
  template <class result_type>
5429
  friend bool ada::parser::try_parse_simple_absolute(std::string_view,
5430
                                                     result_type&);
5431
  template <class result_type>
5432
  friend bool ada::parser::finish_simple_absolute_with_port(
5433
      std::string_view, result_type&, ada::scheme::type, uint32_t, size_t,
5434
      size_t, size_t, bool);
5435
  template <class result_type>
5436
  friend bool ada::parser::try_parse_simple_relative(std::string_view,
5437
                                                     const result_type&,
5438
                                                     result_type&);
5439
5440
  inline void update_unencoded_base_hash(std::string_view input);
5441
  inline void update_base_hostname(std::string_view input);
5442
  inline void update_base_search(std::string_view input,
5443
                                 const uint8_t query_percent_encode_set[]);
5444
  inline void update_base_search(std::optional<std::string>&& input);
5445
  inline void update_base_pathname(std::string_view input);
5446
  inline void update_base_username(std::string_view input);
5447
  inline void update_base_password(std::string_view input);
5448
  inline void update_base_port(std::optional<uint16_t> input);
5449
5450
  /**
5451
   * Returns true if growing href by input_len (worst case) could exceed
5452
   * get_max_input_length(), meaning a rollback snapshot is actually needed.
5453
   */
5454
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
5455
5456
  /**
5457
   * Sets the host or hostname according to override condition.
5458
   * Return true on success.
5459
   * @see https://url.spec.whatwg.org/#hostname-state
5460
   */
5461
  template <bool override_hostname = false>
5462
  bool set_host_or_hostname(std::string_view input);
5463
5464
  /**
5465
   * Return true on success.
5466
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5467
   */
5468
  [[nodiscard]] bool parse_ipv4(std::string_view input);
5469
5470
  /**
5471
   * Return true on success.
5472
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5473
   */
5474
  [[nodiscard]] bool parse_ipv6(std::string_view input);
5475
5476
  /**
5477
   * Return true on success.
5478
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5479
   */
5480
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
5481
5482
  /**
5483
   * A URL's scheme is an ASCII string that identifies the type of URL and can
5484
   * be used to dispatch a URL for further processing after parsing. It is
5485
   * initially the empty string. We only set non_special_scheme when the scheme
5486
   * is non-special, otherwise we avoid constructing string.
5487
   *
5488
   * Special schemes are stored in ada::scheme::details::is_special_list so we
5489
   * typically do not need to store them in each url instance.
5490
   */
5491
  std::string non_special_scheme{};
5492
5493
  /**
5494
   * A URL cannot have a username/password/port if its host is null or the empty
5495
   * string, or its scheme is "file".
5496
   */
5497
  [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5498
5499
  ada_really_inline size_t parse_port(
5500
      std::string_view view, bool check_trailing_content) noexcept override;
5501
5502
2.58k
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5503
2.58k
    return this->parse_port(view, false);
5504
2.58k
  }
5505
5506
  /**
5507
   * Parse the host from the provided input. We assume that
5508
   * the input does not contain spaces or tabs. Control
5509
   * characters and spaces are not trimmed (they should have
5510
   * been removed if needed).
5511
   * Return true on success.
5512
   * @see https://url.spec.whatwg.org/#host-parsing
5513
   */
5514
  [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5515
5516
  template <bool has_state_override = false>
5517
  [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5518
5519
  constexpr void clear_pathname() override;
5520
  constexpr void clear_search() override;
5521
  constexpr void set_protocol_as_file();
5522
5523
  /**
5524
   * Parse the path from the provided input.
5525
   * Return true on success. Control characters not
5526
   * trimmed from the ends (they should have
5527
   * been removed if needed).
5528
   *
5529
   * The input is expected to be UTF-8.
5530
   *
5531
   * @see https://url.spec.whatwg.org/
5532
   */
5533
  ada_really_inline void parse_path(std::string_view input);
5534
5535
  /**
5536
   * Set the scheme for this URL. The provided scheme should be a valid
5537
   * scheme string, be lower-cased, not contain spaces or tabs. It should
5538
   * have no spurious trailing or leading content.
5539
   */
5540
  inline void set_scheme(std::string&& new_scheme) noexcept;
5541
5542
  /**
5543
   * Take the scheme from another URL. The scheme string is moved from the
5544
   * provided url.
5545
   */
5546
  constexpr void copy_scheme(ada::url&& u);
5547
5548
  /**
5549
   * Take the scheme from another URL. The scheme string is copied from the
5550
   * provided url.
5551
   */
5552
  constexpr void copy_scheme(const ada::url& u);
5553
5554
};  // struct url
5555
5556
inline std::ostream& operator<<(std::ostream& out, const ada::url& u);
5557
}  // namespace ada
5558
5559
#endif  // ADA_URL_H
5560
/* end file include/ada/url.h */
5561
5562
namespace ada {
5563
5564
/**
5565
 * Result type for URL parsing operations.
5566
 *
5567
 * Uses `tl::expected` to represent either a successfully parsed URL or an
5568
 * error. This allows for exception-free error handling.
5569
 *
5570
 * @tparam result_type The URL type to return (default: `ada::url_aggregator`)
5571
 *
5572
 * @example
5573
 * ```cpp
5574
 * ada::result<ada::url_aggregator> result = ada::parse("https://example.com");
5575
 * if (result) {
5576
 *     // Success: use result.value() or *result
5577
 * } else {
5578
 *     // Error: handle result.error()
5579
 * }
5580
 * ```
5581
 */
5582
template <class result_type = ada::url_aggregator>
5583
using result = tl::expected<result_type, ada::errors>;
5584
5585
/**
5586
 * Parses a URL string according to the WHATWG URL Standard.
5587
 *
5588
 * This is the main entry point for URL parsing in Ada. The function takes
5589
 * a string input and optionally a base URL for resolving relative URLs.
5590
 *
5591
 * @tparam result_type The URL type to return. Can be either `ada::url` or
5592
 *         `ada::url_aggregator` (default). The `url_aggregator` type is more
5593
 *         memory-efficient as it stores components as offsets into a single
5594
 *         buffer.
5595
 *
5596
 * @param input The URL string to parse. Must be valid ASCII or UTF-8 encoded.
5597
 *        Leading and trailing whitespace is automatically trimmed.
5598
 * @param base_url Optional pointer to a base URL for resolving relative URLs.
5599
 *        If nullptr (default), only absolute URLs can be parsed successfully.
5600
 *
5601
 * @return A `result<result_type>` containing either the parsed URL on success,
5602
 *         or an error code on failure. Use the boolean conversion or
5603
 *         `has_value()` to check for success.
5604
 *
5605
 * @note The parser is fully compliant with the WHATWG URL Standard.
5606
 *
5607
 * Parsing fails if the input or the resulting normalized URL exceeds
5608
 * `get_max_input_length()` bytes (default ~4 GB, configurable via
5609
 * `set_max_input_length()`). This accounts for percent-encoding expansion:
5610
 * a short input that normalizes into a long URL is still rejected.
5611
 *
5612
 * @example
5613
 * ```cpp
5614
 * // Parse an absolute URL
5615
 * auto url = ada::parse("https://user:pass@example.com:8080/path?query#hash");
5616
 * if (url) {
5617
 *     std::cout << url->get_hostname(); // "example.com"
5618
 *     std::cout << url->get_pathname(); // "/path"
5619
 * }
5620
 *
5621
 * // Parse a relative URL with a base
5622
 * auto base = ada::parse("https://example.com/dir/");
5623
 * if (base) {
5624
 *     auto relative = ada::parse("../other/page", &*base);
5625
 *     if (relative) {
5626
 *         std::cout << relative->get_href(); //
5627
 * "https://example.com/other/page"
5628
 *     }
5629
 * }
5630
 * ```
5631
 *
5632
 * @see https://url.spec.whatwg.org/#url-parsing
5633
 */
5634
template <class result_type = ada::url_aggregator>
5635
ada_warn_unused ada::result<result_type> parse(
5636
    std::string_view input, const result_type* base_url = nullptr);
5637
5638
extern template ada::result<url> parse<url>(std::string_view input,
5639
                                            const url* base_url);
5640
extern template ada::result<url_aggregator> parse<url_aggregator>(
5641
    std::string_view input, const url_aggregator* base_url);
5642
5643
/**
5644
 * Checks whether a URL string can be successfully parsed.
5645
 *
5646
 * Equivalent to `parse(input, base).has_value()` for every input, including
5647
 * when `set_max_input_length` rejects a normalized href that exceeds the limit.
5648
 *
5649
 * @param input The URL string to validate. Must be valid ASCII or UTF-8.
5650
 * @param base_input Optional base URL string for relative inputs.
5651
 * @return `true` if parsing would succeed, `false` otherwise.
5652
 * @see https://url.spec.whatwg.org/#dom-url-canparse
5653
 */
5654
bool can_parse(std::string_view input,
5655
               const std::string_view* base_input = nullptr);
5656
5657
#if ADA_INCLUDE_URL_PATTERN
5658
/**
5659
 * Parses a URL pattern according to the URLPattern specification.
5660
 *
5661
 * URL patterns provide a syntax for matching URLs against patterns, similar
5662
 * to how regular expressions match strings. This is useful for routing and
5663
 * URL-based dispatching.
5664
 *
5665
 * @tparam regex_provider The regex implementation to use for pattern matching.
5666
 *
5667
 * @param input Either a URL pattern string (valid UTF-8) or a URLPatternInit
5668
 *        struct specifying individual component patterns.
5669
 * @param base_url Optional pointer to a base URL string (valid UTF-8) for
5670
 *        resolving relative patterns.
5671
 * @param options Optional pointer to configuration options (e.g., ignore_case).
5672
 *
5673
 * @return A `tl::expected` containing either the parsed url_pattern on success,
5674
 *         or an error code on failure.
5675
 *
5676
 * @see https://urlpattern.spec.whatwg.org
5677
 */
5678
template <url_pattern_regex::regex_concept regex_provider>
5679
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
5680
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
5681
                  const std::string_view* base_url = nullptr,
5682
                  const url_pattern_options* options = nullptr);
5683
5684
template <url_pattern_regex::regex_concept regex_provider>
5685
class url_pattern_list;
5686
5687
/**
5688
 * Compiles a set of URLPattern pathname patterns into an ada::url_pattern_list
5689
 * (experimental): one structure that answers "which route matches this
5690
 * pathname, and what are its group values?" without a loop over the
5691
 * patterns and without executing regular expressions for routes written in
5692
 * the static / ":param" / "*" subset.
5693
 *
5694
 * @tparam regex_provider The regex implementation, as for parse_url_pattern;
5695
 *         it is used only for routes that need URLPattern regexp semantics.
5696
 *
5697
 * @param pathname_patterns URLPattern pathname patterns (valid UTF-8), for
5698
 *        example "/users/:id". The index of a pattern is the route index
5699
 *        reported by url_pattern_list::match; duplicates are allowed.
5700
 * @param base_url Optional pointer to a base URL string (valid UTF-8): each
5701
 *        pattern is then processed as the pathname of a URLPatternInit with
5702
 *        that base URL (relative patterns resolve against its path).
5703
 * @param options Optional pointer to configuration options (ignore_case),
5704
 *        applied to every route as parse_url_pattern applies them.
5705
 *
5706
 * @return A `tl::expected` containing either the compiled list on success,
5707
 *         or an error code when a pattern is not a valid URLPattern pathname
5708
 *         pattern (errors::type_error, as the URLPattern constructor).
5709
 *
5710
 * @see https://github.com/whatwg/urlpattern/issues/166
5711
 */
5712
template <url_pattern_regex::regex_concept regex_provider>
5713
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
5714
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
5715
                       const std::string_view* base_url = nullptr,
5716
                       const url_pattern_options* options = nullptr);
5717
5718
/**
5719
 * Compiles the pathname components of existing url_pattern objects into an
5720
 * ada::url_pattern_list (experimental). Only the pathname component of each
5721
 * pattern is used; routes that need the regex provider reuse the pattern's
5722
 * already compiled pathname component. All patterns must share the same
5723
 * ignore_case setting (errors::type_error otherwise); the pathname is
5724
 * compiled as for a special-scheme URL.
5725
 */
5726
template <url_pattern_regex::regex_concept regex_provider>
5727
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
5728
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns);
5729
#endif  // ADA_INCLUDE_URL_PATTERN
5730
5731
/**
5732
 * Converts a file system path to a file:// URL.
5733
 *
5734
 * Creates a properly formatted file URL from a local file system path.
5735
 * Handles platform-specific path separators and percent-encoding.
5736
 *
5737
 * Respects `get_max_input_length()`: if the input path or the resulting
5738
 * `file://` href would exceed the limit, returns an empty string.
5739
 *
5740
 * @param path The file system path to convert. Must be valid ASCII or UTF-8.
5741
 *
5742
 * @return A file:// URL string representing the given path, or empty on
5743
 *         length-limit rejection.
5744
 */
5745
std::string href_from_file(std::string_view path);
5746
5747
/**
5748
 * Sets the maximum allowed length for URLs.
5749
 *
5750
 * Both the raw input and the resulting normalized URL (the href) are checked
5751
 * against this limit. Parsing or setter calls that would produce a URL
5752
 * exceeding this length are rejected. The same limit also applies to
5753
 * `href_from_file` and to query strings passed to `url_search_params`
5754
 * construction / `reset`. The value must fit in a uint32_t.
5755
 * The default is std::numeric_limits<uint32_t>::max() (approximately 4 GB).
5756
 *
5757
 * @param length The new maximum URL length in bytes.
5758
 */
5759
void set_max_input_length(uint32_t length);
5760
5761
/**
5762
 * Returns the current maximum allowed length for URLs.
5763
 *
5764
 * @return The current maximum URL length in bytes.
5765
 */
5766
uint32_t get_max_input_length();
5767
5768
}  // namespace ada
5769
5770
#endif  // ADA_IMPLEMENTATION_H
5771
/* end file include/ada/implementation.h */
5772
5773
#include <cstdint>
5774
#include <optional>
5775
#include <ostream>
5776
#include <string>
5777
#include <string_view>
5778
#include <unordered_map>
5779
#include <utility>
5780
#include <variant>
5781
#include <vector>
5782
5783
#if ADA_TESTING
5784
#include <iostream>
5785
#endif  // ADA_TESTING
5786
5787
#if ADA_INCLUDE_URL_PATTERN
5788
namespace ada {
5789
5790
enum class url_pattern_part_type : uint8_t {
5791
  // The part represents a simple fixed text string.
5792
  FIXED_TEXT,
5793
  // The part represents a matching group with a custom regular expression.
5794
  REGEXP,
5795
  // The part represents a matching group that matches code points up to the
5796
  // next separator code point. This is typically used for a named group like
5797
  // ":foo" that does not have a custom regular expression.
5798
  SEGMENT_WILDCARD,
5799
  // The part represents a matching group that greedily matches all code points.
5800
  // This is typically used for the "*" wildcard matching group.
5801
  FULL_WILDCARD,
5802
};
5803
5804
// Pattern type for fast-path matching optimization.
5805
// This allows skipping expensive regex evaluation for common simple patterns.
5806
enum class url_pattern_component_type : uint8_t {
5807
  // Pattern is "^$" - only matches empty string
5808
  EMPTY,
5809
  // Pattern is "^<literal>$" - exact string match (no regex needed)
5810
  EXACT_MATCH,
5811
  // Pattern is "^(.*)$" - matches anything (full wildcard)
5812
  FULL_WILDCARD,
5813
  // Pattern requires actual regex evaluation
5814
  REGEXP,
5815
};
5816
5817
enum class url_pattern_part_modifier : uint8_t {
5818
  // The part does not have a modifier.
5819
  none,
5820
  // The part has an optional modifier indicated by the U+003F (?) code point.
5821
  optional,
5822
  // The part has a "zero or more" modifier indicated by the U+002A (*) code
5823
  // point.
5824
  zero_or_more,
5825
  // The part has a "one or more" modifier indicated by the U+002B (+) code
5826
  // point.
5827
  one_or_more,
5828
};
5829
5830
// @see https://urlpattern.spec.whatwg.org/#part
5831
class url_pattern_part {
5832
 public:
5833
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5834
                   url_pattern_part_modifier _modifier)
5835
0
      : type(_type), value(std::move(_value)), modifier(_modifier) {}
5836
5837
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5838
                   url_pattern_part_modifier _modifier, std::string&& _name,
5839
                   std::string&& _prefix, std::string&& _suffix)
5840
0
      : type(_type),
5841
0
        value(std::move(_value)),
5842
0
        modifier(_modifier),
5843
0
        name(std::move(_name)),
5844
0
        prefix(std::move(_prefix)),
5845
0
        suffix(std::move(_suffix)) {}
5846
  // A part has an associated type, a string, which must be set upon creation.
5847
  url_pattern_part_type type;
5848
  // A part has an associated value, a string, which must be set upon creation.
5849
  std::string value;
5850
  // A part has an associated modifier a string, which must be set upon
5851
  // creation.
5852
  url_pattern_part_modifier modifier;
5853
  // A part has an associated name, a string, initially the empty string.
5854
  std::string name{};
5855
  // A part has an associated prefix, a string, initially the empty string.
5856
  std::string prefix{};
5857
  // A part has an associated suffix, a string, initially the empty string.
5858
  std::string suffix{};
5859
5860
  inline bool is_regexp() const noexcept;
5861
};
5862
5863
// @see https://urlpattern.spec.whatwg.org/#options-header
5864
struct url_pattern_compile_component_options {
5865
  url_pattern_compile_component_options() = default;
5866
  explicit url_pattern_compile_component_options(
5867
      std::optional<char> new_delimiter = std::nullopt,
5868
      std::optional<char> new_prefix = std::nullopt) noexcept
5869
6
      : delimiter(new_delimiter), prefix(new_prefix) {}
5870
5871
  inline std::string_view get_delimiter() const ada_warn_unused;
5872
  inline std::string_view get_prefix() const ada_warn_unused;
5873
5874
  // @see https://urlpattern.spec.whatwg.org/#options-ignore-case
5875
  bool ignore_case = false;
5876
5877
  static url_pattern_compile_component_options DEFAULT;
5878
  static url_pattern_compile_component_options HOSTNAME;
5879
  static url_pattern_compile_component_options PATHNAME;
5880
5881
 private:
5882
  // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point
5883
  std::optional<char> delimiter{};
5884
  // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point
5885
  std::optional<char> prefix{};
5886
};
5887
5888
// The default options is an options struct with delimiter code point set to
5889
// the empty string and prefix code point set to the empty string.
5890
inline url_pattern_compile_component_options
5891
    url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt);
5892
5893
// The hostname options is an options struct with delimiter code point set
5894
// "." and prefix code point set to the empty string.
5895
inline url_pattern_compile_component_options
5896
    url_pattern_compile_component_options::HOSTNAME('.', std::nullopt);
5897
5898
// The pathname options is an options struct with delimiter code point set
5899
// "/" and prefix code point set to "/".
5900
inline url_pattern_compile_component_options
5901
    url_pattern_compile_component_options::PATHNAME('/', '/');
5902
5903
// A struct providing the URLPattern matching results for a single
5904
// URL component. The URLPatternComponentResult is only ever used
5905
// as a member attribute of a URLPatternResult struct. The
5906
// URLPatternComponentResult API is defined as part of the URLPattern
5907
// specification.
5908
struct url_pattern_component_result {
5909
  std::string input;
5910
  std::unordered_map<std::string, std::optional<std::string>> groups;
5911
5912
  bool operator==(const url_pattern_component_result&) const;
5913
5914
#if ADA_TESTING
5915
  friend void PrintTo(const url_pattern_component_result& result,
5916
                      std::ostream* os) {
5917
    *os << "input: '" << result.input << "', group: ";
5918
    for (const auto& group : result.groups) {
5919
      *os << "(" << group.first << ", " << group.second.value_or("undefined")
5920
          << ") ";
5921
    }
5922
  }
5923
#endif  // ADA_TESTING
5924
};
5925
5926
template <url_pattern_regex::regex_concept regex_provider>
5927
class url_pattern_component {
5928
 public:
5929
  url_pattern_component() = default;
5930
5931
  // This function explicitly takes a std::string because it is moved.
5932
  // To avoid unnecessary copy, move each value while calling the constructor.
5933
  url_pattern_component(std::string&& new_pattern,
5934
                        typename regex_provider::regex_type&& new_regexp,
5935
                        std::vector<std::string>&& new_group_name_list,
5936
                        bool new_has_regexp_groups,
5937
                        url_pattern_component_type new_type,
5938
                        std::string&& new_exact_match_value = {})
5939
      : regexp(std::move(new_regexp)),
5940
        pattern(std::move(new_pattern)),
5941
        group_name_list(std::move(new_group_name_list)),
5942
        exact_match_value(std::move(new_exact_match_value)),
5943
        has_regexp_groups(new_has_regexp_groups),
5944
        type(new_type) {}
5945
5946
  // @see https://urlpattern.spec.whatwg.org/#compile-a-component
5947
  template <url_pattern_encoding_callback F>
5948
  static tl::expected<url_pattern_component, errors> compile(
5949
      std::string_view input, F& encoding_callback,
5950
      url_pattern_compile_component_options& options);
5951
5952
  // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result
5953
  url_pattern_component_result create_component_match_result(
5954
      std::string&& input,
5955
      std::vector<std::optional<std::string>>&& exec_result);
5956
5957
  // Fast path test that returns true/false without constructing result groups.
5958
  // Uses cached pattern type to skip regex evaluation for simple patterns.
5959
  // Note: Not marked noexcept because the regex provider may throw.
5960
  bool fast_test(std::string_view input) const;
5961
5962
  // Fast path match that returns capture groups without regex for simple
5963
  // patterns. Returns nullopt if pattern doesn't match, otherwise returns
5964
  // capture groups.
5965
  std::optional<std::vector<std::optional<std::string>>> fast_match(
5966
      std::string_view input) const;
5967
5968
#if ADA_TESTING
5969
  friend void PrintTo(const url_pattern_component& component,
5970
                      std::ostream* os) {
5971
    *os << "pattern: '" << component.pattern
5972
        << "', has_regexp_groups: " << component.has_regexp_groups
5973
        << "group_name_list: ";
5974
    for (const auto& name : component.group_name_list) {
5975
      *os << name << ", ";
5976
    }
5977
  }
5978
#endif  // ADA_TESTING
5979
5980
  typename regex_provider::regex_type regexp{};
5981
  std::string pattern{};
5982
  std::vector<std::string> group_name_list{};
5983
  // For EXACT_MATCH type: the literal string to compare against
5984
  std::string exact_match_value{};
5985
  bool has_regexp_groups = false;
5986
  // Cached pattern type for fast-path optimization
5987
  url_pattern_component_type type = url_pattern_component_type::REGEXP;
5988
};
5989
5990
// A URLPattern input can be either a string or a URLPatternInit object.
5991
// If it is a string, it must be a valid UTF-8 string.
5992
using url_pattern_input = std::variant<std::string_view, url_pattern_init>;
5993
5994
// A struct providing the URLPattern matching results for all
5995
// components of a URL. The URLPatternResult API is defined as
5996
// part of the URLPattern specification.
5997
struct url_pattern_result {
5998
  std::vector<url_pattern_input> inputs;
5999
  url_pattern_component_result protocol;
6000
  url_pattern_component_result username;
6001
  url_pattern_component_result password;
6002
  url_pattern_component_result hostname;
6003
  url_pattern_component_result port;
6004
  url_pattern_component_result pathname;
6005
  url_pattern_component_result search;
6006
  url_pattern_component_result hash;
6007
};
6008
6009
struct url_pattern_options {
6010
  bool ignore_case = false;
6011
6012
#if ADA_TESTING
6013
  friend void PrintTo(const url_pattern_options& options, std::ostream* os) {
6014
    *os << "ignore_case: '" << options.ignore_case;
6015
  }
6016
#endif  // ADA_TESTING
6017
};
6018
6019
/**
6020
 * @brief URL pattern matching class implementing the URLPattern API.
6021
 *
6022
 * URLPattern provides a way to match URLs against patterns with wildcards
6023
 * and named capture groups. It's useful for routing, URL-based dispatching,
6024
 * and URL validation.
6025
 *
6026
 * Pattern syntax supports:
6027
 * - Literal text matching
6028
 * - Named groups: `:name` (matches up to the next separator)
6029
 * - Wildcards: `*` (matches everything)
6030
 * - Custom regex: `(pattern)`
6031
 * - Optional segments: `:name?`
6032
 * - Repeated segments: `:name+`, `:name*`
6033
 *
6034
 * @tparam regex_provider The regex implementation to use for pattern matching.
6035
 *         Must satisfy the url_pattern_regex::regex_concept.
6036
 *
6037
 * @note All string inputs must be valid UTF-8.
6038
 *
6039
 * @see https://urlpattern.spec.whatwg.org/
6040
 */
6041
template <url_pattern_regex::regex_concept regex_provider>
6042
class url_pattern {
6043
 public:
6044
  url_pattern() = default;
6045
6046
  /**
6047
   * If non-null, base_url must pointer at a valid UTF-8 string.
6048
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec
6049
   */
6050
  result<std::optional<url_pattern_result>> exec(
6051
      const url_pattern_input& input,
6052
      const std::string_view* base_url = nullptr);
6053
6054
  /**
6055
   * If non-null, base_url must pointer at a valid UTF-8 string.
6056
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
6057
   */
6058
  result<bool> test(const url_pattern_input& input,
6059
                    const std::string_view* base_url = nullptr);
6060
6061
  /**
6062
   * @see https://urlpattern.spec.whatwg.org/#url-pattern-match
6063
   * This function expects a valid UTF-8 string if input is a string.
6064
   */
6065
  result<std::optional<url_pattern_result>> match(
6066
      const url_pattern_input& input,
6067
      const std::string_view* base_url_string = nullptr);
6068
6069
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol
6070
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
6071
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username
6072
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
6073
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password
6074
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
6075
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname
6076
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
6077
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port
6078
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
6079
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname
6080
  [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound;
6081
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search
6082
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
6083
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash
6084
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
6085
6086
  // If ignoreCase is true, the JavaScript regular expression created for each
6087
  // pattern must use the `vi` flag. Otherwise, they must use the `v` flag.
6088
  [[nodiscard]] bool ignore_case() const;
6089
6090
  // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups
6091
  [[nodiscard]] bool has_regexp_groups() const;
6092
6093
  // Helper to test all components at once. Returns true if all match.
6094
  [[nodiscard]] bool test_components(
6095
      std::string_view protocol, std::string_view username,
6096
      std::string_view password, std::string_view hostname,
6097
      std::string_view port, std::string_view pathname, std::string_view search,
6098
      std::string_view hash) const;
6099
6100
#if ADA_TESTING
6101
  friend void PrintTo(const url_pattern& c, std::ostream* os) {
6102
    *os << "protocol_component: '" << c.get_protocol() << ", ";
6103
    *os << "username_component: '" << c.get_username() << ", ";
6104
    *os << "password_component: '" << c.get_password() << ", ";
6105
    *os << "hostname_component: '" << c.get_hostname() << ", ";
6106
    *os << "port_component: '" << c.get_port() << ", ";
6107
    *os << "pathname_component: '" << c.get_pathname() << ", ";
6108
    *os << "search_component: '" << c.get_search() << ", ";
6109
    *os << "hash_component: '" << c.get_hash();
6110
  }
6111
#endif  // ADA_TESTING
6112
6113
  template <url_pattern_regex::regex_concept P>
6114
  friend tl::expected<url_pattern<P>, errors> parser::parse_url_pattern_impl(
6115
      std::variant<std::string_view, url_pattern_init>&& input,
6116
      const std::string_view* base_url, const url_pattern_options* options);
6117
6118
  /**
6119
   * @private
6120
   * We can not make this private due to a LLVM bug.
6121
   * Ref: https://github.com/ada-url/ada/pull/859
6122
   */
6123
  url_pattern_component<regex_provider> protocol_component{};
6124
  /**
6125
   * @private
6126
   * We can not make this private due to a LLVM bug.
6127
   * Ref: https://github.com/ada-url/ada/pull/859
6128
   */
6129
  url_pattern_component<regex_provider> username_component{};
6130
  /**
6131
   * @private
6132
   * We can not make this private due to a LLVM bug.
6133
   * Ref: https://github.com/ada-url/ada/pull/859
6134
   */
6135
  url_pattern_component<regex_provider> password_component{};
6136
  /**
6137
   * @private
6138
   * We can not make this private due to a LLVM bug.
6139
   * Ref: https://github.com/ada-url/ada/pull/859
6140
   */
6141
  url_pattern_component<regex_provider> hostname_component{};
6142
  /**
6143
   * @private
6144
   * We can not make this private due to a LLVM bug.
6145
   * Ref: https://github.com/ada-url/ada/pull/859
6146
   */
6147
  url_pattern_component<regex_provider> port_component{};
6148
  /**
6149
   * @private
6150
   * We can not make this private due to a LLVM bug.
6151
   * Ref: https://github.com/ada-url/ada/pull/859
6152
   */
6153
  url_pattern_component<regex_provider> pathname_component{};
6154
  /**
6155
   * @private
6156
   * We can not make this private due to a LLVM bug.
6157
   * Ref: https://github.com/ada-url/ada/pull/859
6158
   */
6159
  url_pattern_component<regex_provider> search_component{};
6160
  /**
6161
   * @private
6162
   * We can not make this private due to a LLVM bug.
6163
   * Ref: https://github.com/ada-url/ada/pull/859
6164
   */
6165
  url_pattern_component<regex_provider> hash_component{};
6166
  /**
6167
   * @private
6168
   * We can not make this private due to a LLVM bug.
6169
   * Ref: https://github.com/ada-url/ada/pull/859
6170
   */
6171
  bool ignore_case_ = false;
6172
};
6173
}  // namespace ada
6174
#endif  // ADA_INCLUDE_URL_PATTERN
6175
#endif
6176
/* end file include/ada/url_pattern.h */
6177
/* begin file include/ada/url_pattern_helpers.h */
6178
/**
6179
 * @file url_pattern_helpers.h
6180
 * @brief Declaration for the URLPattern helpers.
6181
 */
6182
#ifndef ADA_URL_PATTERN_HELPERS_H
6183
#define ADA_URL_PATTERN_HELPERS_H
6184
6185
6186
#include <cstddef>
6187
#include <cstdint>
6188
#include <optional>
6189
#include <string>
6190
#include <string_view>
6191
#include <tuple>
6192
#include <unordered_set>
6193
#include <utility>
6194
#include <vector>
6195
6196
#if ADA_INCLUDE_URL_PATTERN
6197
namespace ada {
6198
enum class errors : uint8_t;
6199
}
6200
6201
namespace ada::url_pattern_helpers {
6202
6203
// @see https://urlpattern.spec.whatwg.org/#token
6204
enum class token_type : uint8_t {
6205
  INVALID_CHAR,    // 0
6206
  OPEN,            // 1
6207
  CLOSE,           // 2
6208
  REGEXP,          // 3
6209
  NAME,            // 4
6210
  CHAR,            // 5
6211
  ESCAPED_CHAR,    // 6
6212
  OTHER_MODIFIER,  // 7
6213
  ASTERISK,        // 8
6214
  END,             // 9
6215
};
6216
6217
#ifdef ADA_TESTING
6218
std::string to_string(token_type type);
6219
#endif  // ADA_TESTING
6220
6221
// @see https://urlpattern.spec.whatwg.org/#tokenize-policy
6222
enum class token_policy {
6223
  strict,
6224
  lenient,
6225
};
6226
6227
// @see https://urlpattern.spec.whatwg.org/#tokens
6228
class token {
6229
 public:
6230
  token(token_type _type, size_t _index, std::string_view _value)
6231
0
      : type(_type), index(_index), value(_value) {}
6232
6233
  // A token has an associated type, a string, initially "invalid-char".
6234
  token_type type = token_type::INVALID_CHAR;
6235
6236
  // A token has an associated index, a number, initially 0. It is the position
6237
  // of the first code point in the pattern string represented by the token.
6238
  size_t index = 0;
6239
6240
  // A token has an associated value, a string, initially the empty string. It
6241
  // contains the code points from the pattern string represented by the token.
6242
  std::string_view value{};
6243
};
6244
6245
// @see https://urlpattern.spec.whatwg.org/#pattern-parser
6246
template <url_pattern_encoding_callback F>
6247
class url_pattern_parser {
6248
 public:
6249
  url_pattern_parser(F& encoding_callback_,
6250
                     std::string_view segment_wildcard_regexp_)
6251
0
      : encoding_callback(encoding_callback_),
6252
0
        segment_wildcard_regexp(segment_wildcard_regexp_) {}
6253
6254
0
  bool can_continue() const { return index < tokens.size(); }
6255
6256
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token
6257
  token* try_consume_token(token_type type);
6258
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token
6259
  token* try_consume_modifier_token();
6260
  // @see
6261
  // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token
6262
  token* try_consume_regexp_or_wildcard_token(const token* name_token);
6263
  // @see https://urlpattern.spec.whatwg.org/#consume-text
6264
  std::string consume_text();
6265
  // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token
6266
  bool consume_required_token(token_type type);
6267
  // @see
6268
  // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value
6269
  std::optional<errors> maybe_add_part_from_the_pending_fixed_value()
6270
      ada_warn_unused;
6271
  // @see https://urlpattern.spec.whatwg.org/#add-a-part
6272
  std::optional<errors> add_part(std::string_view prefix, token* name_token,
6273
                                 token* regexp_or_wildcard_token,
6274
                                 std::string_view suyffix,
6275
                                 token* modifier_token) ada_warn_unused;
6276
6277
  std::vector<token> tokens{};
6278
  F& encoding_callback;
6279
  std::string segment_wildcard_regexp;
6280
  std::vector<url_pattern_part> parts{};
6281
  std::string pending_fixed_value{};
6282
  size_t index = 0;
6283
  size_t next_numeric_name = 0;
6284
  // Names already taken by a part, for the "is a duplicate name" check in
6285
  // add_part. A linear scan of the part list there is O(n^2) over a pattern
6286
  // with n named parts (e.g. "/*" repeated); this set keeps it amortized O(1).
6287
  std::unordered_set<std::string> seen_names{};
6288
};
6289
6290
// @see https://urlpattern.spec.whatwg.org/#tokenizer
6291
class Tokenizer {
6292
 public:
6293
  explicit Tokenizer(std::string_view new_input, token_policy new_policy)
6294
0
      : input(new_input), policy(new_policy) {}
6295
6296
  // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point
6297
  constexpr void get_next_code_point();
6298
6299
  // True when the most recent decoded unit was malformed UTF-8.
6300
0
  bool had_invalid_code_point() const { return invalid_code_point; }
6301
6302
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
6303
  constexpr void seek_and_get_next_code_point(size_t index);
6304
6305
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
6306
6307
  void add_token(token_type type, size_t next_position, size_t value_position,
6308
                 size_t value_length);
6309
6310
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
6311
  void add_token_with_default_length(token_type type, size_t next_position,
6312
                                     size_t value_position);
6313
6314
  // @see
6315
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
6316
  void add_token_with_defaults(token_type type);
6317
6318
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
6319
  std::optional<errors> process_tokenizing_error(
6320
      size_t next_position, size_t value_position) ada_warn_unused;
6321
6322
  friend tl::expected<std::vector<token>, errors> tokenize(
6323
      std::string_view input, token_policy policy);
6324
6325
 private:
6326
  // has an associated input, a pattern string, initially the empty string.
6327
  std::string_view input;
6328
  // has an associated policy, a tokenize policy, initially "strict".
6329
  token_policy policy;
6330
  // has an associated token list, a token list, initially an empty list.
6331
  std::vector<token> token_list{};
6332
  // has an associated index, a number, initially 0.
6333
  size_t index = 0;
6334
  // has an associated next index, a number, initially 0.
6335
  size_t next_index = 0;
6336
  // has an associated code point, a Unicode code point, initially null.
6337
  char32_t code_point{};
6338
  // Tracks whether the last decoded code point was malformed UTF-8.
6339
  bool invalid_code_point = false;
6340
};
6341
6342
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
6343
template <url_pattern_regex::regex_concept regex_provider>
6344
struct constructor_string_parser {
6345
  explicit constructor_string_parser(std::string_view new_input,
6346
                                     std::vector<token>&& new_token_list)
6347
      : input(new_input), token_list(std::move(new_token_list)) {}
6348
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
6349
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
6350
6351
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
6352
  enum class State {
6353
    INIT,
6354
    PROTOCOL,
6355
    AUTHORITY,
6356
    USERNAME,
6357
    PASSWORD,
6358
    HOSTNAME,
6359
    PORT,
6360
    PATHNAME,
6361
    SEARCH,
6362
    HASH,
6363
    DONE,
6364
  };
6365
6366
  // @see
6367
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6368
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6369
6370
 private:
6371
  // @see https://urlpattern.spec.whatwg.org/#rewind
6372
  constexpr void rewind();
6373
6374
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6375
  constexpr bool is_hash_prefix();
6376
6377
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6378
  constexpr bool is_search_prefix();
6379
6380
  // @see https://urlpattern.spec.whatwg.org/#change-state
6381
  void change_state(State state, size_t skip);
6382
6383
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6384
  constexpr bool is_group_open() const;
6385
6386
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6387
  constexpr bool is_group_close() const;
6388
6389
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6390
  constexpr bool is_protocol_suffix() const;
6391
6392
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6393
  constexpr bool next_is_authority_slashes() const;
6394
6395
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6396
  constexpr bool is_an_identity_terminator() const;
6397
6398
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6399
  constexpr bool is_pathname_start() const;
6400
6401
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6402
  constexpr bool is_password_prefix() const;
6403
6404
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6405
  constexpr bool is_an_ipv6_open() const;
6406
6407
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6408
  constexpr bool is_an_ipv6_close() const;
6409
6410
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6411
  constexpr bool is_port_prefix() const;
6412
6413
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6414
  constexpr bool is_non_special_pattern_char(size_t index,
6415
                                             uint32_t value) const;
6416
6417
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6418
  constexpr const token* get_safe_token(size_t index) const;
6419
6420
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6421
  std::string make_component_string();
6422
  // has an associated input, a string, which must be set upon creation.
6423
  std::string_view input;
6424
  // has an associated token list, a token list, which must be set upon
6425
  // creation.
6426
  std::vector<token> token_list;
6427
  // has an associated result, a URLPatternInit, initially set to a new
6428
  // URLPatternInit.
6429
  url_pattern_init result{};
6430
  // has an associated component start, a number, initially set to 0.
6431
  size_t component_start = 0;
6432
  // has an associated token index, a number, initially set to 0.
6433
  size_t token_index = 0;
6434
  // has an associated token increment, a number, initially set to 1.
6435
  size_t token_increment = 1;
6436
  // has an associated group depth, a number, initially set to 0.
6437
  size_t group_depth = 0;
6438
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6439
  // 0.
6440
  size_t hostname_ipv6_bracket_depth = 0;
6441
  // has an associated protocol matches a special scheme flag, a boolean,
6442
  // initially set to false.
6443
  bool protocol_matches_a_special_scheme_flag = false;
6444
  // has an associated state, a string, initially set to "init".
6445
  State state = State::INIT;
6446
};
6447
6448
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6449
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6450
6451
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6452
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6453
6454
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6455
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6456
6457
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6458
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6459
6460
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6461
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6462
    std::string_view input);
6463
6464
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6465
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6466
6467
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6468
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6469
    std::string_view input, std::string_view protocol);
6470
6471
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6472
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6473
6474
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6475
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6476
    std::string_view input);
6477
6478
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6479
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6480
6481
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6482
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6483
6484
// @see https://urlpattern.spec.whatwg.org/#tokenize
6485
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6486
                                                  token_policy policy);
6487
6488
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6489
std::string process_base_url_string(std::string_view input,
6490
                                    url_pattern_init::process_type type);
6491
6492
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6493
std::string escape_pattern_string(std::string_view input);
6494
6495
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6496
std::string escape_regexp_string(std::string_view input);
6497
6498
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6499
constexpr bool is_absolute_pathname(
6500
    std::string_view input, url_pattern_init::process_type type) noexcept;
6501
6502
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6503
template <url_pattern_encoding_callback F>
6504
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6505
    std::string_view input, url_pattern_compile_component_options& options,
6506
    F& encoding_callback);
6507
6508
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6509
std::string generate_pattern_string(
6510
    std::vector<url_pattern_part>& part_list,
6511
    url_pattern_compile_component_options& options);
6512
6513
// @see
6514
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6515
std::tuple<std::string, std::vector<std::string>>
6516
generate_regular_expression_and_name_list(
6517
    const std::vector<url_pattern_part>& part_list,
6518
    url_pattern_compile_component_options options);
6519
6520
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6521
bool is_ipv6_address(std::string_view input) noexcept;
6522
6523
// @see
6524
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6525
template <url_pattern_regex::regex_concept regex_provider>
6526
bool protocol_component_matches_special_scheme(
6527
    ada::url_pattern_component<regex_provider>& input);
6528
6529
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6530
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6531
6532
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6533
std::string generate_segment_wildcard_regexp(
6534
    url_pattern_compile_component_options options);
6535
6536
}  // namespace ada::url_pattern_helpers
6537
#endif  // ADA_INCLUDE_URL_PATTERN
6538
#endif
6539
/* end file include/ada/url_pattern_helpers.h */
6540
6541
#include <cstdint>
6542
#include <string>
6543
#include <string_view>
6544
#include <utility>
6545
#include <variant>
6546
6547
namespace ada::parser {
6548
#if ADA_INCLUDE_URL_PATTERN
6549
template <url_pattern_regex::regex_concept regex_provider>
6550
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6551
    std::variant<std::string_view, url_pattern_init>&& input,
6552
    const std::string_view* base_url, const url_pattern_options* options) {
6553
  // Let init be null.
6554
  url_pattern_init init;
6555
6556
  // If input is a scalar value string then:
6557
  if (std::holds_alternative<std::string_view>(input)) {
6558
    // Set init to the result of running parse a constructor string given input.
6559
    auto parse_result =
6560
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6561
            std::get<std::string_view>(input));
6562
    if (!parse_result) {
6563
      ada_log("constructor_string_parser::parse failed");
6564
      return tl::unexpected(parse_result.error());
6565
    }
6566
    init = std::move(*parse_result);
6567
    // If baseURL is null and init["protocol"] does not exist, then throw a
6568
    // TypeError.
6569
    if (!base_url && !init.protocol) {
6570
      ada_log("base url is null and protocol is not set");
6571
      return tl::unexpected(errors::type_error);
6572
    }
6573
6574
    // If baseURL is not null, set init["baseURL"] to baseURL.
6575
    if (base_url) {
6576
      init.base_url = std::string(*base_url);
6577
    }
6578
  } else {
6579
    // Assert: input is a URLPatternInit.
6580
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6581
    // If baseURL is not null, then throw a TypeError.
6582
    if (base_url) {
6583
      ada_log("base url is not null");
6584
      return tl::unexpected(errors::type_error);
6585
    }
6586
    // Optimization: Avoid copy by moving the input value.
6587
    // Set init to input.
6588
    init = std::move(std::get<url_pattern_init>(input));
6589
  }
6590
6591
  // Let processedInit be the result of process a URLPatternInit given init,
6592
  // "pattern", null, null, null, null, null, null, null, and null.
6593
  auto processed_init =
6594
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6595
  if (!processed_init) {
6596
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6597
    return tl::unexpected(processed_init.error());
6598
  }
6599
6600
  // For each componentName of  "protocol", "username", "password", "hostname",
6601
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6602
  // not exist, then set processedInit[componentName] to "*".
6603
  ADA_ASSERT_TRUE(processed_init.has_value());
6604
  if (!processed_init->protocol) processed_init->protocol = "*";
6605
  if (!processed_init->username) processed_init->username = "*";
6606
  if (!processed_init->password) processed_init->password = "*";
6607
  if (!processed_init->hostname) processed_init->hostname = "*";
6608
  if (!processed_init->port) processed_init->port = "*";
6609
  if (!processed_init->pathname) processed_init->pathname = "*";
6610
  if (!processed_init->search) processed_init->search = "*";
6611
  if (!processed_init->hash) processed_init->hash = "*";
6612
6613
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6614
  ada_log("-- processed_init->username: ", processed_init->username.value());
6615
  ada_log("-- processed_init->password: ", processed_init->password.value());
6616
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6617
  ada_log("-- processed_init->port: ", processed_init->port.value());
6618
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6619
  ada_log("-- processed_init->search: ", processed_init->search.value());
6620
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6621
6622
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6623
  // is a string which represents its corresponding default port in radix-10
6624
  // using ASCII digits then set processedInit["port"] to the empty string.
6625
  // TODO: Optimization opportunity.
6626
  if (scheme::is_special(*processed_init->protocol)) {
6627
    // file is the only special scheme with no default port; get_special_port
6628
    // returns 0 as its sentinel, so a literal "0" port must not be mistaken for
6629
    // a default and dropped.
6630
    uint16_t default_port = scheme::get_special_port(*processed_init->protocol);
6631
    if (default_port != 0 &&
6632
        std::to_string(default_port) == processed_init->port.value()) {
6633
      processed_init->port->clear();
6634
    }
6635
  }
6636
6637
  // Let urlPattern be a new URL pattern.
6638
  url_pattern<regex_provider> url_pattern_{};
6639
6640
  // Set urlPattern's protocol component to the result of compiling a component
6641
  // given processedInit["protocol"], canonicalize a protocol, and default
6642
  // options.
6643
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6644
      processed_init->protocol.value(),
6645
      url_pattern_helpers::canonicalize_protocol,
6646
      url_pattern_compile_component_options::DEFAULT);
6647
  if (!protocol_component) {
6648
    ada_log("url_pattern_component::compile failed for protocol ",
6649
            processed_init->protocol.value());
6650
    return tl::unexpected(protocol_component.error());
6651
  }
6652
  url_pattern_.protocol_component = std::move(*protocol_component);
6653
6654
  // Set urlPattern's username component to the result of compiling a component
6655
  // given processedInit["username"], canonicalize a username, and default
6656
  // options.
6657
  auto username_component = url_pattern_component<regex_provider>::compile(
6658
      processed_init->username.value(),
6659
      url_pattern_helpers::canonicalize_username,
6660
      url_pattern_compile_component_options::DEFAULT);
6661
  if (!username_component) {
6662
    ada_log("url_pattern_component::compile failed for username ",
6663
            processed_init->username.value());
6664
    return tl::unexpected(username_component.error());
6665
  }
6666
  url_pattern_.username_component = std::move(*username_component);
6667
6668
  // Set urlPattern's password component to the result of compiling a component
6669
  // given processedInit["password"], canonicalize a password, and default
6670
  // options.
6671
  auto password_component = url_pattern_component<regex_provider>::compile(
6672
      processed_init->password.value(),
6673
      url_pattern_helpers::canonicalize_password,
6674
      url_pattern_compile_component_options::DEFAULT);
6675
  if (!password_component) {
6676
    ada_log("url_pattern_component::compile failed for password ",
6677
            processed_init->password.value());
6678
    return tl::unexpected(password_component.error());
6679
  }
6680
  url_pattern_.password_component = std::move(*password_component);
6681
6682
  // TODO: Optimization opportunity. The following if statement can be
6683
  // simplified.
6684
  // If the result running hostname pattern is an IPv6 address given
6685
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6686
  // to the result of compiling a component given processedInit["hostname"],
6687
  // canonicalize an IPv6 hostname, and hostname options.
6688
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6689
    ada_log("processed_init->hostname is ipv6 address");
6690
    // then set urlPattern's hostname component to the result of compiling a
6691
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6692
    // and hostname options.
6693
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6694
        processed_init->hostname.value(),
6695
        url_pattern_helpers::canonicalize_ipv6_hostname,
6696
        url_pattern_compile_component_options::DEFAULT);
6697
    if (!hostname_component) {
6698
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6699
              processed_init->hostname.value());
6700
      return tl::unexpected(hostname_component.error());
6701
    }
6702
    url_pattern_.hostname_component = std::move(*hostname_component);
6703
  } else {
6704
    // Otherwise, set urlPattern's hostname component to the result of compiling
6705
    // a component given processedInit["hostname"], canonicalize a hostname, and
6706
    // hostname options.
6707
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6708
        processed_init->hostname.value(),
6709
        url_pattern_helpers::canonicalize_hostname,
6710
        url_pattern_compile_component_options::HOSTNAME);
6711
    if (!hostname_component) {
6712
      ada_log("url_pattern_component::compile failed for hostname ",
6713
              processed_init->hostname.value());
6714
      return tl::unexpected(hostname_component.error());
6715
    }
6716
    url_pattern_.hostname_component = std::move(*hostname_component);
6717
  }
6718
6719
  // Set urlPattern's port component to the result of compiling a component
6720
  // given processedInit["port"], canonicalize a port, and default options.
6721
  auto port_component = url_pattern_component<regex_provider>::compile(
6722
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6723
      url_pattern_compile_component_options::DEFAULT);
6724
  if (!port_component) {
6725
    ada_log("url_pattern_component::compile failed for port ",
6726
            processed_init->port.value());
6727
    return tl::unexpected(port_component.error());
6728
  }
6729
  url_pattern_.port_component = std::move(*port_component);
6730
6731
  // Let compileOptions be a copy of the default options with the ignore case
6732
  // property set to options["ignoreCase"].
6733
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6734
  if (options) {
6735
    compile_options.ignore_case = options->ignore_case;
6736
    // Recorded on the pattern so that url_pattern::ignore_case() reports the
6737
    // option it was created with.
6738
    url_pattern_.ignore_case_ = options->ignore_case;
6739
  }
6740
6741
  // TODO: Optimization opportunity: Simplify this if statement.
6742
  // If the result of running protocol component matches a special scheme given
6743
  // urlPattern's protocol component is true, then:
6744
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6745
          regex_provider>(url_pattern_.protocol_component)) {
6746
    // Let pathCompileOptions be copy of the pathname options with the ignore
6747
    // case property set to options["ignoreCase"].
6748
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6749
    if (options) {
6750
      path_compile_options.ignore_case = options->ignore_case;
6751
    }
6752
6753
    // Set urlPattern's pathname component to the result of compiling a
6754
    // component given processedInit["pathname"], canonicalize a pathname, and
6755
    // pathCompileOptions.
6756
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6757
        processed_init->pathname.value(),
6758
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6759
    if (!pathname_component) {
6760
      ada_log("url_pattern_component::compile failed for pathname ",
6761
              processed_init->pathname.value());
6762
      return tl::unexpected(pathname_component.error());
6763
    }
6764
    url_pattern_.pathname_component = std::move(*pathname_component);
6765
  } else {
6766
    // Otherwise set urlPattern's pathname component to the result of compiling
6767
    // a component given processedInit["pathname"], canonicalize an opaque
6768
    // pathname, and compileOptions.
6769
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6770
        processed_init->pathname.value(),
6771
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6772
    if (!pathname_component) {
6773
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6774
              processed_init->pathname.value());
6775
      return tl::unexpected(pathname_component.error());
6776
    }
6777
    url_pattern_.pathname_component = std::move(*pathname_component);
6778
  }
6779
6780
  // Set urlPattern's search component to the result of compiling a component
6781
  // given processedInit["search"], canonicalize a search, and compileOptions.
6782
  auto search_component = url_pattern_component<regex_provider>::compile(
6783
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6784
      compile_options);
6785
  if (!search_component) {
6786
    ada_log("url_pattern_component::compile failed for search ",
6787
            processed_init->search.value());
6788
    return tl::unexpected(search_component.error());
6789
  }
6790
  url_pattern_.search_component = std::move(*search_component);
6791
6792
  // Set urlPattern's hash component to the result of compiling a component
6793
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6794
  auto hash_component = url_pattern_component<regex_provider>::compile(
6795
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6796
      compile_options);
6797
  if (!hash_component) {
6798
    ada_log("url_pattern_component::compile failed for hash ",
6799
            processed_init->hash.value());
6800
    return tl::unexpected(hash_component.error());
6801
  }
6802
  url_pattern_.hash_component = std::move(*hash_component);
6803
6804
  // Return urlPattern.
6805
  return url_pattern_;
6806
}
6807
#endif  // ADA_INCLUDE_URL_PATTERN
6808
6809
}  // namespace ada::parser
6810
6811
#endif  // ADA_PARSER_INL_H
6812
/* end file include/ada/parser-inl.h */
6813
/* begin file include/ada/scheme-inl.h */
6814
/**
6815
 * @file scheme-inl.h
6816
 * @brief Definitions for the URL scheme.
6817
 */
6818
#ifndef ADA_SCHEME_INL_H
6819
#define ADA_SCHEME_INL_H
6820
6821
6822
#include <cstddef>
6823
#include <cstdint>
6824
#include <string_view>
6825
6826
namespace ada::scheme {
6827
6828
/**
6829
 * @namespace ada::scheme::details
6830
 * @brief Includes the definitions for scheme specific entities
6831
 */
6832
namespace details {
6833
// for use with is_special and get_special_port
6834
// Spaces, if present, are removed from URL.
6835
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6836
                                                "ftp",  "wss", "file",  " "};
6837
// for use with get_special_port
6838
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6839
6840
// @private
6841
// convert a string_view to a 64-bit integer key for fast comparison
6842
0
constexpr uint64_t make_key(std::string_view sv) {
6843
0
  uint64_t val = 0;
6844
0
  for (size_t i = 0; i < sv.size(); i++)
6845
0
    val |= (uint64_t)(uint8_t)sv[i] << (i * 8);
6846
0
  return val;
6847
0
}
6848
// precomputed keys for the special schemes, indexed by a hash of the input
6849
// string
6850
constexpr uint64_t scheme_keys[] = {
6851
    make_key("http"),   // 0: HTTP
6852
    0,                  // 1: sentinel
6853
    make_key("https"),  // 2: HTTPS
6854
    make_key("ws"),     // 3: WS
6855
    make_key("ftp"),    // 4: FTP
6856
    make_key("wss"),    // 5: WSS
6857
    make_key("file"),   // 6: FILE
6858
    0,                  // 7: sentinel
6859
};
6860
6861
// @private
6862
// branchless load of up to 5 characters into a uint64_t, padding with zeros if
6863
// n < 5
6864
130k
inline uint64_t branchless_load5(const char* p, size_t n) {
6865
130k
  uint64_t input = (uint8_t)p[0];
6866
130k
  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
6867
130k
  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
6868
130k
  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
6869
130k
  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
6870
130k
  return input;
6871
130k
}
6872
}  // namespace details
6873
6874
/****
6875
 * @private
6876
 * In is_special, get_scheme_type, and get_special_port, we
6877
 * use a standard hashing technique to find the index of the scheme in
6878
 * the is_special_list. The hashing technique is based on the size of
6879
 * the scheme and the first character of the scheme. It ensures that we
6880
 * do at most one string comparison per call. If the protocol is
6881
 * predictible (e.g., it is always "http"), we can get a better average
6882
 * performance by using a simpler approach where we loop and compare
6883
 * scheme with all possible protocols starting with the most likely
6884
 * protocol. Doing multiple comparisons may have a poor worst case
6885
 * performance, however. In this instance, we choose a potentially
6886
 * slightly lower best-case performance for a better worst-case
6887
 * performance. We can revisit this choice at any time.
6888
 *
6889
 * Reference:
6890
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6891
 * More C++ gems 17 (2000).
6892
 *
6893
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6894
 *
6895
 * Reference: https://github.com/ada-url/ada/issues/617
6896
 ****/
6897
6898
9.28k
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6899
9.28k
  if (scheme.empty()) {
6900
0
    return false;
6901
0
  }
6902
9.28k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6903
9.28k
  const std::string_view target = details::is_special_list[hash_value];
6904
9.28k
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6905
9.28k
}
6906
0
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6907
0
  if (scheme.empty()) {
6908
0
    return 0;
6909
0
  }
6910
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6911
0
  const std::string_view target = details::is_special_list[hash_value];
6912
0
  if (scheme.size() == target.size() &&
6913
0
      details::branchless_load5(scheme.data(), scheme.size()) ==
6914
0
          details::scheme_keys[hash_value]) {
6915
0
    return details::special_ports[hash_value];
6916
0
  } else {
6917
0
    return 0;
6918
0
  }
6919
0
}
6920
74.2k
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6921
74.2k
  return details::special_ports[int(type)];
6922
74.2k
}
6923
184k
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6924
184k
  if (scheme.empty()) {
6925
0
    return ada::scheme::NOT_SPECIAL;
6926
0
  }
6927
184k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6928
184k
  const std::string_view target = details::is_special_list[hash_value];
6929
184k
  if (scheme.size() == target.size() &&
6930
130k
      details::branchless_load5(scheme.data(), scheme.size()) ==
6931
130k
          details::scheme_keys[hash_value]) {
6932
104k
    return ada::scheme::type(hash_value);
6933
104k
  } else {
6934
79.9k
    return ada::scheme::NOT_SPECIAL;
6935
79.9k
  }
6936
184k
}
6937
6938
}  // namespace ada::scheme
6939
6940
#endif  // ADA_SCHEME_INL_H
6941
/* end file include/ada/scheme-inl.h */
6942
/* begin file include/ada/serializers.h */
6943
/**
6944
 * @file serializers.h
6945
 * @brief IP address serialization utilities.
6946
 *
6947
 * This header provides functions for converting IP addresses to their
6948
 * string representations according to the WHATWG URL Standard.
6949
 */
6950
#ifndef ADA_SERIALIZERS_H
6951
#define ADA_SERIALIZERS_H
6952
6953
6954
#include <array>
6955
#include <cstddef>
6956
#include <cstdint>
6957
#include <string>
6958
6959
/**
6960
 * @namespace ada::serializers
6961
 * @brief IP address serialization functions.
6962
 *
6963
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6964
 */
6965
namespace ada::serializers {
6966
6967
/**
6968
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6969
 * Used for :: compression in IPv6 serialization.
6970
 *
6971
 * @param address The 8 16-bit pieces of the IPv6 address.
6972
 * @param[out] compress Index of the start of the longest zero sequence.
6973
 * @param[out] compress_length Length of the longest zero sequence.
6974
 */
6975
void find_longest_sequence_of_ipv6_pieces(
6976
    const std::array<uint16_t, 8>& address, size_t& compress,
6977
    size_t& compress_length) noexcept;
6978
6979
/**
6980
 * Serializes an IPv6 address to its string representation.
6981
 *
6982
 * @param address The 8 16-bit pieces of the IPv6 address.
6983
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6984
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6985
 */
6986
std::string ipv6(const std::array<uint16_t, 8>& address);
6987
6988
/**
6989
 * Serializes an IPv4 address to its dotted-decimal string representation.
6990
 *
6991
 * @param address The 32-bit IPv4 address as an integer.
6992
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6993
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6994
 */
6995
std::string ipv4(uint64_t address);
6996
6997
}  // namespace ada::serializers
6998
6999
#endif  // ADA_SERIALIZERS_H
7000
/* end file include/ada/serializers.h */
7001
/* begin file include/ada/state.h */
7002
/**
7003
 * @file state.h
7004
 * @brief URL parser state machine states.
7005
 *
7006
 * Defines the states used by the URL parsing state machine as specified
7007
 * in the WHATWG URL Standard.
7008
 *
7009
 * @see https://url.spec.whatwg.org/#url-parsing
7010
 */
7011
#ifndef ADA_STATE_H
7012
#define ADA_STATE_H
7013
7014
7015
#include <string>
7016
7017
namespace ada {
7018
7019
/**
7020
 * @brief States in the URL parsing state machine.
7021
 *
7022
 * The URL parser processes input through a sequence of states, each handling
7023
 * a specific part of the URL syntax.
7024
 *
7025
 * @see https://url.spec.whatwg.org/#url-parsing
7026
 */
7027
enum class state {
7028
  /**
7029
   * @see https://url.spec.whatwg.org/#authority-state
7030
   */
7031
  AUTHORITY,
7032
7033
  /**
7034
   * @see https://url.spec.whatwg.org/#scheme-start-state
7035
   */
7036
  SCHEME_START,
7037
7038
  /**
7039
   * @see https://url.spec.whatwg.org/#scheme-state
7040
   */
7041
  SCHEME,
7042
7043
  /**
7044
   * @see https://url.spec.whatwg.org/#host-state
7045
   */
7046
  HOST,
7047
7048
  /**
7049
   * @see https://url.spec.whatwg.org/#no-scheme-state
7050
   */
7051
  NO_SCHEME,
7052
7053
  /**
7054
   * @see https://url.spec.whatwg.org/#fragment-state
7055
   */
7056
  FRAGMENT,
7057
7058
  /**
7059
   * @see https://url.spec.whatwg.org/#relative-state
7060
   */
7061
  RELATIVE_SCHEME,
7062
7063
  /**
7064
   * @see https://url.spec.whatwg.org/#relative-slash-state
7065
   */
7066
  RELATIVE_SLASH,
7067
7068
  /**
7069
   * @see https://url.spec.whatwg.org/#file-state
7070
   */
7071
  FILE,
7072
7073
  /**
7074
   * @see https://url.spec.whatwg.org/#file-host-state
7075
   */
7076
  FILE_HOST,
7077
7078
  /**
7079
   * @see https://url.spec.whatwg.org/#file-slash-state
7080
   */
7081
  FILE_SLASH,
7082
7083
  /**
7084
   * @see https://url.spec.whatwg.org/#path-or-authority-state
7085
   */
7086
  PATH_OR_AUTHORITY,
7087
7088
  /**
7089
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
7090
   */
7091
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
7092
7093
  /**
7094
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
7095
   */
7096
  SPECIAL_AUTHORITY_SLASHES,
7097
7098
  /**
7099
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
7100
   */
7101
  SPECIAL_RELATIVE_OR_AUTHORITY,
7102
7103
  /**
7104
   * @see https://url.spec.whatwg.org/#query-state
7105
   */
7106
  QUERY,
7107
7108
  /**
7109
   * @see https://url.spec.whatwg.org/#path-state
7110
   */
7111
  PATH,
7112
7113
  /**
7114
   * @see https://url.spec.whatwg.org/#path-start-state
7115
   */
7116
  PATH_START,
7117
7118
  /**
7119
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
7120
   */
7121
  OPAQUE_PATH,
7122
7123
  /**
7124
   * @see https://url.spec.whatwg.org/#port-state
7125
   */
7126
  PORT,
7127
};
7128
7129
/**
7130
 * Converts a parser state to its string name for debugging.
7131
 * @param s The state to convert.
7132
 * @return A string representation of the state.
7133
 */
7134
ada_warn_unused std::string to_string(ada::state s);
7135
7136
}  // namespace ada
7137
7138
#endif  // ADA_STATE_H
7139
/* end file include/ada/state.h */
7140
/* begin file include/ada/unicode.h */
7141
/**
7142
 * @file unicode.h
7143
 * @brief Definitions for all unicode specific functions.
7144
 */
7145
#ifndef ADA_UNICODE_H
7146
#define ADA_UNICODE_H
7147
7148
7149
#include <cstddef>
7150
#include <cstdint>
7151
#include <optional>
7152
#include <string>
7153
#include <string_view>
7154
7155
/**
7156
 * Unicode operations. These functions are not part of our public API and may
7157
 * change at any time.
7158
 *
7159
 * @private
7160
 * @namespace ada::unicode
7161
 * @brief Includes the definitions for unicode operations
7162
 */
7163
namespace ada::unicode {
7164
7165
/**
7166
 * @private
7167
 * We receive a UTF-8 string representing a domain name.
7168
 * If the string is percent encoded, we apply percent decoding.
7169
 *
7170
 * Given a domain, we need to identify its labels.
7171
 * They are separated by label-separators:
7172
 *
7173
 * U+002E (.) FULL STOP
7174
 * U+FF0E FULLWIDTH FULL STOP
7175
 * U+3002 IDEOGRAPHIC FULL STOP
7176
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
7177
 *
7178
 * They are all mapped to U+002E.
7179
 *
7180
 * We process each label into a string that should not exceed 63 octets.
7181
 * If the string is already punycode (starts with "xn--"), then we must
7182
 * scan it to look for unallowed code points.
7183
 * Otherwise, if the string is not pure ASCII, we need to transcode it
7184
 * to punycode by following RFC 3454 which requires us to
7185
 * - Map characters  (see section 3),
7186
 * - Normalize (see section 4),
7187
 * - Reject forbidden characters,
7188
 * - Check for right-to-left characters and if so, check all requirements (see
7189
 * section 6),
7190
 * - Optionally reject based on unassigned code points (section 7).
7191
 *
7192
 * The Unicode standard provides a table of code points with a mapping, a list
7193
 * of forbidden code points and so forth. This table is subject to change and
7194
 * will vary based on the implementation. For Unicode 15, the table is at
7195
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
7196
 * If you use ICU, they parse this table and map it to code using a Python
7197
 * script.
7198
 *
7199
 * The resulting strings should not exceed 255 octets according to RFC 1035
7200
 * section 2.3.4. ICU checks for label size and domain size, but these errors
7201
 * are ignored.
7202
 *
7203
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
7204
 *
7205
 */
7206
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
7207
              size_t first_percent);
7208
7209
/**
7210
 * @private
7211
 * Checks if the input has tab or newline characters.
7212
 *
7213
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
7214
 * enough that compilers like GCC can 'autovectorize it'.
7215
 */
7216
ada_really_inline bool has_tabs_or_newline(
7217
    std::string_view user_input) noexcept;
7218
7219
/**
7220
 * @private
7221
 * Checks if the input is a forbidden host code point.
7222
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
7223
 */
7224
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
7225
7226
/**
7227
 * @private
7228
 * Checks if the input contains a forbidden domain code point.
7229
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7230
 */
7231
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
7232
    const char* input, size_t length) noexcept;
7233
7234
/**
7235
 * @private
7236
 * Checks if the input contains a forbidden domain code point in which case
7237
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
7238
 * then the second bit is set to 1.
7239
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7240
 */
7241
ada_really_inline constexpr uint8_t
7242
contains_forbidden_domain_code_point_or_upper(const char* input,
7243
                                              size_t length) noexcept;
7244
7245
/**
7246
 * @private
7247
 * Checks if the input is a forbidden domain code point.
7248
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7249
 */
7250
ada_really_inline constexpr bool is_forbidden_domain_code_point(
7251
    char c) noexcept;
7252
7253
/**
7254
 * @private
7255
 * Checks if the input is alphanumeric, '+', '-' or '.'
7256
 */
7257
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
7258
7259
/**
7260
 * @private
7261
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
7262
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
7263
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
7264
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
7265
 */
7266
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
7267
7268
/**
7269
 * @private
7270
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
7271
 * inclusive.
7272
 */
7273
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
7274
7275
/**
7276
 * @private
7277
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
7278
 * character.
7279
 */
7280
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
7281
7282
/**
7283
 * @private
7284
 * Checks if the input is a C0 control or space character.
7285
 *
7286
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
7287
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
7288
 * SEPARATOR ONE, inclusive.
7289
 */
7290
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
7291
7292
/**
7293
 * @private
7294
 * Checks if the input is a ASCII tab or newline character.
7295
 *
7296
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
7297
 */
7298
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
7299
7300
/**
7301
 * @private
7302
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
7303
 * match for ".%2e", "%2e.", or "%2e%2e".
7304
 */
7305
ada_really_inline constexpr bool is_double_dot_path_segment(
7306
    std::string_view input) noexcept;
7307
7308
/**
7309
 * @private
7310
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
7311
 * match for "%2e".
7312
 */
7313
ada_really_inline constexpr bool is_single_dot_path_segment(
7314
    std::string_view input) noexcept;
7315
7316
/**
7317
 * @private
7318
 * @details ipv4 character might contain 0-9 or a-f character ranges.
7319
 */
7320
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
7321
7322
/**
7323
 * @private
7324
 * @details Convert hex to binary. Caller is responsible to ensure that
7325
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
7326
 */
7327
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
7328
7329
/**
7330
 * @private
7331
 * first_percent should be  = input.find('%')
7332
 *
7333
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
7334
 * @author Node.js
7335
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
7336
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
7337
 */
7338
std::string percent_decode(std::string_view input, size_t first_percent);
7339
7340
/**
7341
 * Decode an application/x-www-form-urlencoded component: map '+' to space,
7342
 * then percent-decode. Single allocation; no intermediate string.
7343
 *
7344
 * @param input A form-urlencoded component (key or value).
7345
 * @return The decoded string.
7346
 * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
7347
 */
7348
std::string form_urlencoded_decode(std::string_view input);
7349
7350
/**
7351
 * @private
7352
 * Returns a percent-encoding string whether percent encoding was needed or not.
7353
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7354
 */
7355
std::string percent_encode(std::string_view input,
7356
                           const uint8_t character_set[]);
7357
/**
7358
 * @private
7359
 * Returns a percent-encoded string version of input, while starting the percent
7360
 * encoding at the provided index.
7361
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7362
 */
7363
std::string percent_encode(std::string_view input,
7364
                           const uint8_t character_set[], size_t index);
7365
/**
7366
 * @private
7367
 * Returns true if percent encoding was needed, in which case, we store
7368
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
7369
 * true, the content is appended to 'out'.
7370
 * If percent encoding is not needed, out is left unchanged.
7371
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7372
 */
7373
template <bool append>
7374
bool percent_encode(std::string_view input, const uint8_t character_set[],
7375
                    std::string& out);
7376
/**
7377
 * @private
7378
 * Returns the index at which percent encoding should start, or (equivalently),
7379
 * the length of the prefix that does not require percent encoding.
7380
 */
7381
ada_really_inline size_t percent_encode_index(std::string_view input,
7382
                                              const uint8_t character_set[]);
7383
/**
7384
 * @private
7385
 * Lowers the string in-place, assuming that the content is ASCII.
7386
 * Return true if the content was ASCII.
7387
 */
7388
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
7389
}  // namespace ada::unicode
7390
7391
#endif  // ADA_UNICODE_H
7392
/* end file include/ada/unicode.h */
7393
/* begin file include/ada/url_base-inl.h */
7394
/**
7395
 * @file url_base-inl.h
7396
 * @brief Inline functions for url base
7397
 */
7398
#ifndef ADA_URL_BASE_INL_H
7399
#define ADA_URL_BASE_INL_H
7400
7401
7402
#include <cstdint>
7403
#include <string>
7404
#if ADA_REGULAR_VISUAL_STUDIO
7405
#include <intrin.h>
7406
#endif  // ADA_REGULAR_VISUAL_STUDIO
7407
7408
namespace ada {
7409
7410
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
7411
1.83M
    const noexcept {
7412
1.83M
  return type != ada::scheme::NOT_SPECIAL;
7413
1.83M
}
7414
7415
14.7k
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
7416
14.7k
  return ada::scheme::get_special_port(type);
7417
14.7k
}
7418
7419
[[nodiscard]] ada_really_inline uint16_t
7420
52.5k
url_base::scheme_default_port() const noexcept {
7421
52.5k
  return scheme::get_special_port(type);
7422
52.5k
}
7423
7424
}  // namespace ada
7425
7426
#endif  // ADA_URL_BASE_INL_H
7427
/* end file include/ada/url_base-inl.h */
7428
/* begin file include/ada/url-inl.h */
7429
/**
7430
 * @file url-inl.h
7431
 * @brief Definitions for the URL
7432
 */
7433
#ifndef ADA_URL_INL_H
7434
#define ADA_URL_INL_H
7435
7436
7437
#include <charconv>
7438
#include <cstdint>
7439
#include <cstring>
7440
#include <optional>
7441
#include <ostream>
7442
#include <string>
7443
#include <string_view>
7444
#include <system_error>
7445
#include <utility>
7446
#if ADA_REGULAR_VISUAL_STUDIO
7447
#include <intrin.h>
7448
#endif  // ADA_REGULAR_VISUAL_STUDIO
7449
7450
namespace ada {
7451
401k
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7452
401k
  return !username.empty() || !password.empty();
7453
401k
}
7454
32.4k
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7455
32.4k
  return port.has_value();
7456
32.4k
}
7457
73.3k
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7458
73.3k
  return !host.has_value() || host->empty() || type == ada::scheme::type::FILE;
7459
73.3k
}
7460
32.4k
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7461
32.4k
  if (!host.has_value()) {
7462
3.70k
    return false;
7463
3.70k
  }
7464
28.7k
  return host->empty();
7465
32.4k
}
7466
32.4k
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7467
32.4k
  return host.has_value();
7468
32.4k
}
7469
0
inline std::ostream& operator<<(std::ostream& out, const ada::url& u) {
7470
0
  return out << u.to_string();
7471
0
}
7472
7473
32.4k
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7474
32.4k
  return path.size();
7475
32.4k
}
7476
7477
40.1k
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7478
40.1k
  return path;
7479
40.1k
}
7480
7481
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7482
32.4k
    const {
7483
32.4k
  url_components out{};
7484
7485
  // protocol ends with ':'. for example: "https:"
7486
32.4k
  out.protocol_end = uint32_t(get_protocol().size());
7487
7488
  // Trailing index is always the next character of the current one.
7489
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7490
32.4k
  size_t running_index = out.protocol_end;
7491
7492
32.4k
  if (host.has_value()) {
7493
    // 2 characters for "//" and 1 character for starting index
7494
28.7k
    out.host_start = out.protocol_end + 2;
7495
7496
28.7k
    if (has_credentials()) {
7497
16.4k
      out.username_end = uint32_t(out.host_start + username.size());
7498
7499
16.4k
      out.host_start += uint32_t(username.size());
7500
7501
16.4k
      if (!password.empty()) {
7502
16.1k
        out.host_start += uint32_t(password.size() + 1);
7503
16.1k
      }
7504
7505
      // host_start is the '@' when there are credentials, exactly as
7506
      // url_aggregator reports it, so the host itself begins one byte later.
7507
16.4k
      out.host_end = uint32_t(out.host_start + 1 + host->size());
7508
16.4k
    } else {
7509
12.3k
      out.username_end = out.host_start;
7510
7511
      // Host does not start with "@" if it does not include credentials.
7512
12.3k
      out.host_end = uint32_t(out.host_start + host->size());
7513
12.3k
    }
7514
7515
    // host_end is one past the host, so it is already the next index.
7516
28.7k
    running_index = out.host_end;
7517
28.7k
  } else {
7518
    // Update host start and end date to the same index, since it does not
7519
    // exist.
7520
3.70k
    out.host_start = out.protocol_end;
7521
3.70k
    out.host_end = out.host_start;
7522
7523
3.70k
    if (!has_opaque_path && path.starts_with("//")) {
7524
      // If url's host is null, url does not have an opaque path, url's path's
7525
      // size is greater than 1, and url's path[0] is the empty string, then
7526
      // append U+002F (/) followed by U+002E (.) to output.
7527
39
      running_index = out.protocol_end + 2;
7528
3.67k
    } else {
7529
3.67k
      running_index = out.protocol_end;
7530
3.67k
    }
7531
3.70k
  }
7532
7533
32.4k
  if (port.has_value()) {
7534
2.51k
    out.port = *port;
7535
2.51k
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7536
2.51k
  }
7537
7538
32.4k
  out.pathname_start = uint32_t(running_index);
7539
7540
32.4k
  running_index += path.size();
7541
7542
32.4k
  if (query.has_value()) {
7543
18.1k
    out.search_start = uint32_t(running_index);
7544
18.1k
    running_index += get_search().size();
7545
18.1k
    if (get_search().empty()) {
7546
640
      running_index++;
7547
640
    }
7548
18.1k
  }
7549
7550
32.4k
  if (hash.has_value()) {
7551
18.0k
    out.hash_start = uint32_t(running_index);
7552
18.0k
  }
7553
7554
32.4k
  return out;
7555
32.4k
}
7556
7557
199
inline void url::update_base_hostname(std::string_view input) { host = input; }
7558
7559
24.4k
inline void url::update_unencoded_base_hash(std::string_view input) {
7560
  // We do the percent encoding
7561
24.4k
  hash = unicode::percent_encode(input,
7562
24.4k
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7563
24.4k
}
7564
7565
inline void url::update_base_search(std::string_view input,
7566
24.6k
                                    const uint8_t query_percent_encode_set[]) {
7567
24.6k
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7568
24.6k
}
7569
7570
0
inline void url::update_base_search(std::optional<std::string>&& input) {
7571
0
  query = std::move(input);
7572
0
}
7573
7574
17.2k
inline void url::update_base_pathname(const std::string_view input) {
7575
17.2k
  path = input;
7576
17.2k
}
7577
7578
0
inline void url::update_base_username(const std::string_view input) {
7579
0
  username = input;
7580
0
}
7581
7582
0
inline void url::update_base_password(const std::string_view input) {
7583
0
  password = input;
7584
0
}
7585
7586
0
inline void url::update_base_port(std::optional<uint16_t> input) {
7587
0
  port = input;
7588
0
}
7589
7590
46
constexpr void url::clear_pathname() { path.clear(); }
7591
7592
992
constexpr void url::clear_search() { query = std::nullopt; }
7593
7594
32.4k
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7595
32.4k
  return hash.has_value();
7596
32.4k
}
7597
7598
64.9k
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7599
64.9k
  return query.has_value();
7600
64.9k
}
7601
7602
4.27k
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7603
7604
17.6k
inline void url::set_scheme(std::string&& new_scheme) noexcept {
7605
17.6k
  type = ada::scheme::get_scheme_type(new_scheme);
7606
  // We only move the 'scheme' if it is non-special.
7607
17.6k
  if (!is_special()) {
7608
11.4k
    non_special_scheme = std::move(new_scheme);
7609
11.4k
  }
7610
17.6k
}
7611
7612
0
constexpr void url::copy_scheme(ada::url&& u) {
7613
0
  non_special_scheme = u.non_special_scheme;
7614
0
  type = u.type;
7615
0
}
7616
7617
1.77k
constexpr void url::copy_scheme(const ada::url& u) {
7618
1.77k
  non_special_scheme = u.non_special_scheme;
7619
1.77k
  type = u.type;
7620
1.77k
}
7621
7622
197k
[[nodiscard]] ada_really_inline std::string url::get_href() const {
7623
197k
  if (is_special() && host.has_value() && username.empty() &&
7624
127k
      password.empty() && !port.has_value()) [[likely]] {
7625
117k
    const std::string_view scheme = ada::scheme::details::is_special_list[type];
7626
117k
    const size_t host_size = host->size();
7627
117k
    const size_t path_size = path.size();
7628
117k
    const size_t query_size = query.has_value() ? query->size() : 0;
7629
117k
    const size_t hash_size = hash.has_value() ? hash->size() : 0;
7630
117k
    const size_t total = scheme.size() + 3 + host_size + path_size +
7631
117k
                         (query.has_value() ? query_size + 1 : 0) +
7632
117k
                         (hash.has_value() ? hash_size + 1 : 0);
7633
117k
    std::string output(total, '\0');
7634
117k
    char* p = output.data();
7635
117k
    std::memcpy(p, scheme.data(), scheme.size());
7636
117k
    p += scheme.size();
7637
117k
    p[0] = ':';
7638
117k
    p[1] = '/';
7639
117k
    p[2] = '/';
7640
117k
    p += 3;
7641
    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7642
117k
    std::memcpy(p, host->data(), host_size);
7643
117k
    p += host_size;
7644
117k
    std::memcpy(p, path.data(), path_size);
7645
117k
    p += path_size;
7646
117k
    if (query.has_value()) {
7647
19.5k
      *p++ = '?';
7648
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7649
19.5k
      std::memcpy(p, query->data(), query_size);
7650
19.5k
      p += query_size;
7651
19.5k
    }
7652
117k
    if (hash.has_value()) {
7653
18.6k
      *p++ = '#';
7654
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7655
18.6k
      std::memcpy(p, hash->data(), hash_size);
7656
18.6k
    }
7657
117k
    return output;
7658
117k
  }
7659
7660
79.5k
  std::string output;
7661
79.5k
  output.reserve(get_href_size());
7662
7663
79.5k
  if (is_special()) {
7664
46.8k
    output.append(ada::scheme::details::is_special_list[type]);
7665
46.8k
    output += ':';
7666
46.8k
  } else {
7667
32.7k
    output.append(non_special_scheme);
7668
32.7k
    output += ':';
7669
32.7k
  }
7670
7671
79.5k
  if (host.has_value()) {
7672
50.8k
    output += '/';
7673
50.8k
    output += '/';
7674
50.8k
    if (has_credentials()) {
7675
39.2k
      output.append(username);
7676
39.2k
      if (!password.empty()) {
7677
34.5k
        output += ':';
7678
34.5k
        output.append(password);
7679
34.5k
      }
7680
39.2k
      output += '@';
7681
39.2k
    }
7682
50.8k
    output.append(*host);
7683
50.8k
    if (port.has_value()) {
7684
41.6k
      output += ':';
7685
41.6k
      char port_buf[5];
7686
41.6k
      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
7687
41.6k
      (void)ec;
7688
41.6k
      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
7689
41.6k
    }
7690
50.8k
  } else if (!has_opaque_path && path.starts_with("//")) {
7691
    // If url's host is null, url does not have an opaque path, url's path's
7692
    // size is greater than 1, and url's path[0] is the empty string, then
7693
    // append U+002F (/) followed by U+002E (.) to output.
7694
195
    output += '/';
7695
195
    output += '.';
7696
195
  }
7697
79.5k
  output.append(path);
7698
79.5k
  if (query.has_value()) {
7699
40.3k
    output += '?';
7700
40.3k
    output.append(*query);
7701
40.3k
  }
7702
79.5k
  if (hash.has_value()) {
7703
39.6k
    output += '#';
7704
39.6k
    output.append(*hash);
7705
39.6k
  }
7706
79.5k
  return output;
7707
197k
}
7708
7709
293k
[[nodiscard]] inline size_t url::get_href_size() const noexcept {
7710
293k
  size_t size = 0;
7711
293k
  if (is_special()) {
7712
243k
    size += ada::scheme::details::is_special_list[type].size() + 1;
7713
243k
  } else {
7714
49.4k
    size += non_special_scheme.size() + 1;
7715
49.4k
  }
7716
293k
  if (host.has_value()) {
7717
249k
    size += host->size();
7718
249k
    size += 2;
7719
249k
    if (has_credentials()) {
7720
165k
      size += username.size();
7721
165k
      if (!password.empty()) {
7722
141k
        size += 1 + password.size();
7723
141k
      }
7724
165k
      size += 1;
7725
165k
    }
7726
249k
    if (port.has_value()) {
7727
79.3k
      size += 1;
7728
79.3k
      uint16_t p = *port;
7729
79.3k
      size += (p >= 10000)  ? 5
7730
79.3k
              : (p >= 1000) ? 4
7731
78.3k
              : (p >= 100)  ? 3
7732
14.0k
              : (p >= 10)   ? 2
7733
13.2k
                            : 1;
7734
79.3k
    }
7735
249k
  } else if (!has_opaque_path && path.starts_with("//")) {
7736
316
    size += 2;
7737
316
  }
7738
293k
  size += path.size();
7739
293k
  if (query.has_value()) {
7740
116k
    size += 1 + query->size();
7741
116k
  }
7742
293k
  if (hash.has_value()) {
7743
97.9k
    size += 1 + hash->size();
7744
97.9k
  }
7745
293k
  return size;
7746
293k
}
7747
7748
ada_really_inline size_t url::parse_port(std::string_view view,
7749
25.7k
                                         bool check_trailing_content) noexcept {
7750
25.7k
  ada_log("parse_port('", view, "') ", view.size());
7751
25.7k
  if (!view.empty() && view[0] == '-') {
7752
22
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7753
22
    is_valid = false;
7754
22
    return 0;
7755
22
  }
7756
25.7k
  uint16_t parsed_port{};
7757
25.7k
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7758
25.7k
  if (r.ec == std::errc::result_out_of_range) {
7759
266
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7760
266
    is_valid = false;
7761
266
    return 0;
7762
266
  }
7763
25.4k
  ada_log("parse_port: ", parsed_port);
7764
25.4k
  const auto consumed = size_t(r.ptr - view.data());
7765
25.4k
  ada_log("parse_port: consumed ", consumed);
7766
25.4k
  if (check_trailing_content) {
7767
23.1k
    is_valid &=
7768
23.1k
        (consumed == view.size() || view[consumed] == '/' ||
7769
516
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7770
23.1k
  }
7771
25.4k
  ada_log("parse_port: is_valid = ", is_valid);
7772
25.4k
  if (is_valid) {
7773
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7774
25.2k
    auto default_port = scheme_default_port();
7775
25.2k
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7776
25.1k
                         (default_port != parsed_port);
7777
25.2k
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7778
25.2k
                                                  : std::nullopt;
7779
25.2k
  }
7780
25.4k
  return consumed;
7781
25.7k
}
7782
7783
}  // namespace ada
7784
7785
#endif  // ADA_URL_H
7786
/* end file include/ada/url-inl.h */
7787
/* begin file include/ada/url_components-inl.h */
7788
/**
7789
 * @file url_components.h
7790
 * @brief Declaration for the URL Components
7791
 */
7792
#ifndef ADA_URL_COMPONENTS_INL_H
7793
#define ADA_URL_COMPONENTS_INL_H
7794
7795
7796
#include <cstdint>
7797
7798
namespace ada {
7799
7800
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7801
125k
    const noexcept {
7802
  /**
7803
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7804
   *       |     |    |          | ^^^^|       |   |
7805
   *       |     |    |          | |   |       |   `----- hash_start
7806
   *       |     |    |          | |   |       `--------- search_start
7807
   *       |     |    |          | |   `----------------- pathname_start
7808
   *       |     |    |          | `--------------------- port
7809
   *       |     |    |          `----------------------- host_end
7810
   *       |     |    `---------------------------------- host_start
7811
   *       |     `--------------------------------------- username_end
7812
   *       `--------------------------------------------- protocol_end
7813
   */
7814
  // These conditions can be made more strict.
7815
125k
  if (protocol_end == url_components::omitted) {
7816
0
    return false;
7817
0
  }
7818
125k
  uint32_t index = protocol_end;
7819
7820
125k
  if (username_end == url_components::omitted) {
7821
0
    return false;
7822
0
  }
7823
125k
  if (username_end < index) {
7824
0
    return false;
7825
0
  }
7826
125k
  index = username_end;
7827
7828
125k
  if (host_start == url_components::omitted) {
7829
0
    return false;
7830
0
  }
7831
125k
  if (host_start < index) {
7832
0
    return false;
7833
0
  }
7834
125k
  index = host_start;
7835
7836
125k
  if (port != url_components::omitted) {
7837
38.2k
    if (port > 0xffff) {
7838
0
      return false;
7839
0
    }
7840
38.2k
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7841
38.2k
    if (index + port_length < index) {
7842
0
      return false;
7843
0
    }
7844
38.2k
    index += port_length;
7845
38.2k
  }
7846
7847
125k
  if (pathname_start == url_components::omitted) {
7848
0
    return false;
7849
0
  }
7850
125k
  if (pathname_start < index) {
7851
0
    return false;
7852
0
  }
7853
125k
  index = pathname_start;
7854
7855
125k
  if (search_start != url_components::omitted) {
7856
86.1k
    if (search_start < index) {
7857
0
      return false;
7858
0
    }
7859
86.1k
    index = search_start;
7860
86.1k
  }
7861
7862
125k
  if (hash_start != url_components::omitted) {
7863
63.1k
    if (hash_start < index) {
7864
0
      return false;
7865
0
    }
7866
63.1k
  }
7867
7868
125k
  return true;
7869
125k
}
7870
7871
}  // namespace ada
7872
#endif
7873
/* end file include/ada/url_components-inl.h */
7874
/* begin file include/ada/url_aggregator.h */
7875
/**
7876
 * @file url_aggregator.h
7877
 * @brief Declaration for the `ada::url_aggregator` class.
7878
 *
7879
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7880
 * URL using a single buffer with component offsets. This is the default and
7881
 * most memory-efficient URL representation in Ada.
7882
 *
7883
 * @see url.h for an alternative representation using separate strings
7884
 */
7885
#ifndef ADA_URL_AGGREGATOR_H
7886
#define ADA_URL_AGGREGATOR_H
7887
7888
#include <cstddef>
7889
#include <cstdint>
7890
#include <ostream>
7891
#include <string>
7892
#include <string_view>
7893
#include <variant>
7894
7895
7896
namespace ada {
7897
7898
namespace parser {}
7899
7900
/**
7901
 * @brief Memory-efficient URL representation using a single buffer.
7902
 *
7903
 * The `url_aggregator` stores the entire normalized URL in a single string
7904
 * buffer and tracks component boundaries using offsets. This design minimizes
7905
 * memory allocations and is ideal for read-mostly access patterns.
7906
 *
7907
 * Getter methods return `std::string_view` pointing into the internal buffer.
7908
 * These views are lightweight (no allocation) but become invalid if the
7909
 * url_aggregator is modified or destroyed.
7910
 *
7911
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7912
 * when any setter is called. Do not use a getter's result as input to a
7913
 * setter on the same object without copying first.
7914
 *
7915
 * @note This is the default URL type returned by `ada::parse()`.
7916
 *
7917
 * @see url For an alternative using separate std::string instances
7918
 */
7919
struct url_aggregator : url_base {
7920
216k
  url_aggregator() = default;
7921
7.40k
  url_aggregator(const url_aggregator& u) = default;
7922
174k
  url_aggregator(url_aggregator&& u) noexcept = default;
7923
14.8k
  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
7924
10.1k
  url_aggregator& operator=(const url_aggregator& u) = default;
7925
398k
  ~url_aggregator() override = default;
7926
7927
  /**
7928
   * The setter functions follow the steps defined in the URL Standard.
7929
   *
7930
   * The url_aggregator has a single buffer that contains the entire normalized
7931
   * URL. The various components are represented as offsets into that buffer.
7932
   * When you call get_pathname(), for example, you get a std::string_view that
7933
   * points into that buffer. If the url_aggregator is modified, the buffer may
7934
   * be reallocated, and the std::string_view you obtained earlier may become
7935
   * invalid. In particular, this implies that you cannot modify the URL using
7936
   * a setter function with a std::string_view that points into the
7937
   * url_aggregator E.g., the following is incorrect:
7938
   * url->set_hostname(url->get_pathname()).
7939
   * You must first copy the pathname to a separate string.
7940
   * std::string pathname(url->get_pathname());
7941
   * url->set_hostname(pathname);
7942
   *
7943
   * The caller is responsible for ensuring that the url_aggregator is not
7944
   * modified while any std::string_view obtained from it is in use.
7945
   */
7946
  bool set_href(std::string_view input);
7947
  bool set_host(std::string_view input);
7948
  bool set_hostname(std::string_view input);
7949
  bool set_protocol(std::string_view input);
7950
  bool set_username(std::string_view input);
7951
  bool set_password(std::string_view input);
7952
  bool set_port(std::string_view input);
7953
  bool set_pathname(std::string_view input);
7954
  void set_search(std::string_view input);
7955
  void set_hash(std::string_view input);
7956
7957
  /**
7958
   * Validates whether the hostname is a valid domain according to RFC 1034.
7959
   * @return `true` if the domain is valid, `false` otherwise.
7960
   */
7961
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7962
7963
  /**
7964
   * Returns the URL's origin (scheme + host + port for special URLs).
7965
   * @return A newly allocated string containing the serialized origin.
7966
   * @see https://url.spec.whatwg.org/#concept-url-origin
7967
   */
7968
  [[nodiscard]] std::string get_origin() const override;
7969
7970
  /**
7971
   * Returns the full serialized URL (the href) as a string_view.
7972
   * Does not allocate memory. The returned view becomes invalid if this
7973
   * url_aggregator is modified or destroyed.
7974
   * @return A string_view into the internal buffer.
7975
   * @see https://url.spec.whatwg.org/#dom-url-href
7976
   */
7977
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7978
      ada_lifetime_bound;
7979
7980
  /**
7981
   * Returns the byte length of the serialized URL without allocating a string.
7982
   * @return Size of the href in bytes.
7983
   */
7984
  [[nodiscard]] constexpr size_t get_href_size() const noexcept;
7985
7986
  /**
7987
   * Returns the URL's username component.
7988
   * Does not allocate memory. The returned view becomes invalid if this
7989
   * url_aggregator is modified or destroyed.
7990
   * @return A string_view of the username.
7991
   * @see https://url.spec.whatwg.org/#dom-url-username
7992
   */
7993
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
7994
7995
  /**
7996
   * Returns the URL's password component.
7997
   * Does not allocate memory. The returned view becomes invalid if this
7998
   * url_aggregator is modified or destroyed.
7999
   * @return A string_view of the password.
8000
   * @see https://url.spec.whatwg.org/#dom-url-password
8001
   */
8002
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
8003
8004
  /**
8005
   * Returns the URL's port as a string (e.g., "8080").
8006
   * Does not allocate memory. Returns empty view if no port is set.
8007
   * The returned view becomes invalid if this url_aggregator is modified.
8008
   * @return A string_view of the port.
8009
   * @see https://url.spec.whatwg.org/#dom-url-port
8010
   */
8011
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
8012
8013
  /**
8014
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
8015
   * Does not allocate memory. Returns empty view if no fragment is set.
8016
   * The returned view becomes invalid if this url_aggregator is modified.
8017
   * @return A string_view of the hash.
8018
   * @see https://url.spec.whatwg.org/#dom-url-hash
8019
   */
8020
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
8021
8022
  /**
8023
   * Returns the URL's host and port (e.g., "example.com:8080").
8024
   * Does not allocate memory. Returns empty view if no host is set.
8025
   * The returned view becomes invalid if this url_aggregator is modified.
8026
   * @return A string_view of host:port.
8027
   * @see https://url.spec.whatwg.org/#dom-url-host
8028
   */
8029
  [[nodiscard]] std::string_view get_host() const ada_lifetime_bound;
8030
8031
  /**
8032
   * Returns the URL's hostname (without port).
8033
   * Does not allocate memory. Returns empty view if no host is set.
8034
   * The returned view becomes invalid if this url_aggregator is modified.
8035
   * @return A string_view of the hostname.
8036
   * @see https://url.spec.whatwg.org/#dom-url-hostname
8037
   */
8038
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
8039
8040
  /**
8041
   * Returns the URL's path component.
8042
   * Does not allocate memory. The returned view becomes invalid if this
8043
   * url_aggregator is modified or destroyed.
8044
   * @return A string_view of the pathname.
8045
   * @see https://url.spec.whatwg.org/#dom-url-pathname
8046
   */
8047
  [[nodiscard]] constexpr std::string_view get_pathname() const
8048
      ada_lifetime_bound;
8049
8050
  /**
8051
   * Returns the byte length of the pathname without creating a string.
8052
   * @return Size of the pathname in bytes.
8053
   * @see https://url.spec.whatwg.org/#dom-url-pathname
8054
   */
8055
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
8056
8057
  /**
8058
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
8059
   * Does not allocate memory. Returns empty view if no query is set.
8060
   * The returned view becomes invalid if this url_aggregator is modified.
8061
   * @return A string_view of the search/query.
8062
   * @see https://url.spec.whatwg.org/#dom-url-search
8063
   */
8064
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
8065
8066
  /**
8067
   * Returns the URL's scheme followed by a colon (e.g., "https:").
8068
   * Does not allocate memory. The returned view becomes invalid if this
8069
   * url_aggregator is modified or destroyed.
8070
   * @return A string_view of the protocol.
8071
   * @see https://url.spec.whatwg.org/#dom-url-protocol
8072
   */
8073
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
8074
8075
  /**
8076
   * Checks if the URL has credentials (non-empty username or password).
8077
   * @return `true` if username or password is non-empty, `false` otherwise.
8078
   */
8079
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
8080
      const noexcept;
8081
8082
  /**
8083
   * Returns the URL component offsets for efficient serialization.
8084
   *
8085
   * The components represent byte offsets into the serialized URL:
8086
   * ```
8087
   * https://user:pass@example.com:1234/foo/bar?baz#quux
8088
   *       |     |    |          | ^^^^|       |   |
8089
   *       |     |    |          | |   |       |   `----- hash_start
8090
   *       |     |    |          | |   |       `--------- search_start
8091
   *       |     |    |          | |   `----------------- pathname_start
8092
   *       |     |    |          | `--------------------- port
8093
   *       |     |    |          `----------------------- host_end
8094
   *       |     |    `---------------------------------- host_start
8095
   *       |     `--------------------------------------- username_end
8096
   *       `--------------------------------------------- protocol_end
8097
   * ```
8098
   * @return A constant reference to the url_components struct.
8099
   * @see https://github.com/servo/rust-url
8100
   */
8101
  [[nodiscard]] ada_really_inline const url_components& get_components()
8102
      const noexcept;
8103
8104
  /**
8105
   * Returns a JSON string representation of this URL for debugging.
8106
   * @return A JSON-formatted string with all URL components.
8107
   */
8108
  [[nodiscard]] std::string to_string() const override;
8109
8110
  /**
8111
   * Returns a visual diagram showing component boundaries in the URL.
8112
   * Useful for debugging and understanding URL structure.
8113
   * @return A multi-line string diagram.
8114
   */
8115
  [[nodiscard]] std::string to_diagram() const;
8116
8117
  /**
8118
   * Validates internal consistency of component offsets (for debugging).
8119
   * @return `true` if offsets are consistent, `false` if corrupted.
8120
   */
8121
  [[nodiscard]] constexpr bool validate() const noexcept;
8122
8123
  /**
8124
   * Checks if the URL has an empty hostname (host is set but empty string).
8125
   * @return `true` if host exists but is empty, `false` otherwise.
8126
   */
8127
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
8128
8129
  /**
8130
   * Checks if the URL has a hostname (including empty hostnames).
8131
   * @return `true` if host is present, `false` otherwise.
8132
   */
8133
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
8134
8135
  /**
8136
   * Checks if the URL has a non-empty username.
8137
   * @return `true` if username is non-empty, `false` otherwise.
8138
   */
8139
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
8140
8141
  /**
8142
   * Checks if the URL has a non-empty password.
8143
   * @return `true` if password is non-empty, `false` otherwise.
8144
   */
8145
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
8146
8147
  /**
8148
   * Checks if the URL has a non-default port explicitly specified.
8149
   * @return `true` if a port is present, `false` otherwise.
8150
   */
8151
  [[nodiscard]] constexpr bool has_port() const noexcept;
8152
8153
  /**
8154
   * Checks if the URL has a password component (may be empty).
8155
   * @return `true` if password is present, `false` otherwise.
8156
   */
8157
  [[nodiscard]] constexpr bool has_password() const noexcept;
8158
8159
  /**
8160
   * Checks if the URL has a fragment/hash component.
8161
   * @return `true` if hash is present, `false` otherwise.
8162
   */
8163
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
8164
8165
  /**
8166
   * Checks if the URL has a query/search component.
8167
   * @return `true` if query is present, `false` otherwise.
8168
   */
8169
  [[nodiscard]] constexpr bool has_search() const noexcept override;
8170
8171
  /**
8172
   * Removes the port from the URL.
8173
   */
8174
  inline void clear_port();
8175
8176
  /**
8177
   * Removes the hash/fragment from the URL.
8178
   */
8179
  inline void clear_hash();
8180
8181
  /**
8182
   * Removes the query/search string from the URL.
8183
   */
8184
  inline void clear_search() override;
8185
8186
 private:
8187
  // helper methods
8188
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
8189
      url_aggregator& url);
8190
  // parse_url methods
8191
  friend url_aggregator parser::parse_url<url_aggregator>(
8192
      std::string_view, const url_aggregator*);
8193
8194
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
8195
      std::string_view, const url_aggregator*);
8196
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
8197
      std::string_view, const url_aggregator*);
8198
  template <class result_type>
8199
  friend bool parser::try_parse_simple_absolute(std::string_view, result_type&);
8200
  template <class result_type>
8201
  friend bool parser::finish_simple_absolute_with_port(std::string_view,
8202
                                                       result_type&,
8203
                                                       ada::scheme::type,
8204
                                                       uint32_t, size_t, size_t,
8205
                                                       size_t, bool);
8206
  template <class result_type>
8207
  friend bool parser::try_parse_simple_relative(std::string_view,
8208
                                                const result_type&,
8209
                                                result_type&);
8210
8211
#if ADA_INCLUDE_URL_PATTERN
8212
  // url_pattern methods
8213
  template <url_pattern_regex::regex_concept regex_provider>
8214
  friend tl::expected<url_pattern<regex_provider>, errors>
8215
  parse_url_pattern_impl(
8216
      std::variant<std::string_view, url_pattern_init>&& input,
8217
      const std::string_view* base_url, const url_pattern_options* options);
8218
#endif  // ADA_INCLUDE_URL_PATTERN
8219
8220
  // components is declared before buffer so that the offset fields land
8221
  // close to url_base in memory, improving cache locality for getter calls.
8222
  // Note: exact cache-line placement is implementation- and platform-dependent.
8223
  url_components components{};
8224
  std::string buffer{};
8225
8226
  /**
8227
   * Returns true if neither the search, nor the hash nor the pathname
8228
   * have been set.
8229
   * @return true if the buffer is ready to receive the path.
8230
   */
8231
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
8232
8233
  inline void add_authority_slashes_if_needed();
8234
8235
  /**
8236
   * To optimize performance, you may indicate how much memory to allocate
8237
   * within this instance.
8238
   */
8239
  constexpr void reserve(uint32_t capacity);
8240
8241
  ada_really_inline size_t parse_port(std::string_view view,
8242
                                      bool check_trailing_content) override;
8243
8244
2.58k
  ada_really_inline size_t parse_port(std::string_view view) override {
8245
2.58k
    return this->parse_port(view, false);
8246
2.58k
  }
8247
8248
  /**
8249
   * Return true on success. The 'in_place' parameter indicates whether the
8250
   * the string_view input is pointing in the buffer. When in_place is false,
8251
   * we must nearly always update the buffer.
8252
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
8253
   */
8254
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
8255
8256
  /**
8257
   * Return true on success.
8258
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
8259
   */
8260
  [[nodiscard]] bool parse_ipv6(std::string_view input);
8261
8262
  /**
8263
   * Return true on success.
8264
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
8265
   */
8266
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
8267
8268
  ada_really_inline void parse_path(std::string_view input);
8269
8270
  /**
8271
   * A URL cannot have a username/password/port if its host is null or the empty
8272
   * string, or its scheme is "file".
8273
   */
8274
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
8275
8276
  /**
8277
   * @private
8278
   * Several setters restore a saved copy of the URL when the mutation pushes
8279
   * the buffer past get_max_input_length(). A setter grows the buffer by at
8280
   * most input_len * 3 (worst-case percent-encoding) plus a small constant for
8281
   * delimiters, so this returns false when that upper bound stays within the
8282
   * limit. It does not account for parse-failure rollbacks, which are
8283
   * unrelated to the length limit.
8284
   */
8285
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
8286
8287
  template <bool override_hostname = false>
8288
  bool set_host_or_hostname(std::string_view input);
8289
8290
  ada_really_inline bool parse_host(std::string_view input);
8291
8292
  inline void update_base_authority(std::string_view base_buffer,
8293
                                    const url_components& base);
8294
  inline void update_unencoded_base_hash(std::string_view input);
8295
  inline void update_base_hostname(std::string_view input);
8296
  inline void update_base_search(std::string_view input);
8297
  inline void update_base_search(std::string_view input,
8298
                                 const uint8_t* query_percent_encode_set);
8299
  inline void update_base_pathname(std::string_view input);
8300
  inline void update_base_username(std::string_view input);
8301
  inline void append_base_username(std::string_view input);
8302
  inline void update_base_password(std::string_view input);
8303
  inline void append_base_password(std::string_view input);
8304
  inline void update_base_port(uint32_t input);
8305
  inline void append_base_pathname(std::string_view input);
8306
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
8307
  constexpr void clear_hostname();
8308
  constexpr void clear_password();
8309
  constexpr void clear_pathname() override;
8310
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
8311
  void delete_dash_dot();
8312
  inline void consume_prepared_path(std::string_view input);
8313
  template <bool has_state_override = false>
8314
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
8315
      std::string_view input);
8316
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
8317
                                                std::string_view input);
8318
  [[nodiscard]] constexpr bool has_authority() const noexcept;
8319
  constexpr void set_protocol_as_file();
8320
  inline void set_scheme(std::string_view new_scheme);
8321
  /**
8322
   * Fast function to set the scheme from a view with a colon in the
8323
   * buffer, does not change type.
8324
   */
8325
  inline void set_scheme_from_view_with_colon(
8326
      std::string_view new_scheme_with_colon);
8327
  inline void copy_scheme(const url_aggregator& u);
8328
8329
  inline void update_host_to_base_host(const std::string_view input);
8330
8331
};  // url_aggregator
8332
8333
inline std::ostream& operator<<(std::ostream& out, const url& u);
8334
}  // namespace ada
8335
8336
#endif
8337
/* end file include/ada/url_aggregator.h */
8338
/* begin file include/ada/url_aggregator-inl.h */
8339
/**
8340
 * @file url_aggregator-inl.h
8341
 * @brief Inline functions for url aggregator
8342
 */
8343
#ifndef ADA_URL_AGGREGATOR_INL_H
8344
#define ADA_URL_AGGREGATOR_INL_H
8345
8346
/* begin file include/ada/unicode-inl.h */
8347
/**
8348
 * @file unicode-inl.h
8349
 * @brief Definitions for unicode operations.
8350
 */
8351
#ifndef ADA_UNICODE_INL_H
8352
#define ADA_UNICODE_INL_H
8353
8354
#include <cstddef>
8355
#include <cstdint>
8356
#include <cstring>
8357
#include <string_view>
8358
8359
/**
8360
 * Unicode operations. These functions are not part of our public API and may
8361
 * change at any time.
8362
 *
8363
 * private
8364
 * @namespace ada::unicode
8365
 * @brief Includes the declarations for unicode operations
8366
 */
8367
namespace ada::unicode {
8368
ada_really_inline size_t percent_encode_index(const std::string_view input,
8369
44.7k
                                              const uint8_t character_set[]) {
8370
44.7k
  const char* data = input.data();
8371
44.7k
  const size_t size = input.size();
8372
8373
  // Process 8 bytes at a time using unrolled loop
8374
44.7k
  size_t i = 0;
8375
78.8k
  for (; i + 8 <= size; i += 8) {
8376
53.1k
    unsigned char chunk[8];
8377
53.1k
    std::memcpy(&chunk, data + i,
8378
53.1k
                8);  // entices compiler to unconditionally process 8 characters
8379
8380
    // Check 8 characters at once
8381
364k
    for (size_t j = 0; j < 8; j++) {
8382
330k
      if (character_sets::bit_at(character_set, chunk[j])) {
8383
18.9k
        return i + j;
8384
18.9k
      }
8385
330k
    }
8386
53.1k
  }
8387
8388
  // Handle remaining bytes
8389
50.7k
  for (; i < size; i++) {
8390
36.2k
    if (character_sets::bit_at(character_set, data[i])) {
8391
11.2k
      return i;
8392
11.2k
    }
8393
36.2k
  }
8394
8395
14.4k
  return size;
8396
25.7k
}
8397
}  // namespace ada::unicode
8398
8399
#endif  // ADA_UNICODE_INL_H
8400
/* end file include/ada/unicode-inl.h */
8401
8402
#include <charconv>
8403
#include <cstdint>
8404
#include <cstring>
8405
#include <ostream>
8406
#include <string>
8407
#include <string_view>
8408
#include <system_error>
8409
8410
namespace ada {
8411
8412
inline void url_aggregator::update_base_authority(
8413
14.2k
    std::string_view base_buffer, const ada::url_components& base) {
8414
14.2k
  std::string_view input = base_buffer.substr(
8415
14.2k
      base.protocol_end, base.host_start - base.protocol_end);
8416
14.2k
  ada_log("url_aggregator::update_base_authority ", input);
8417
8418
14.2k
  bool input_starts_with_dash = input.starts_with("//");
8419
14.2k
  uint32_t diff = components.host_start - components.protocol_end;
8420
8421
14.2k
  buffer.erase(components.protocol_end,
8422
14.2k
               components.host_start - components.protocol_end);
8423
14.2k
  components.username_end = components.protocol_end;
8424
8425
14.2k
  if (input_starts_with_dash) {
8426
13.3k
    input.remove_prefix(2);
8427
13.3k
    diff += 2;  // add "//"
8428
13.3k
    buffer.insert(components.protocol_end, "//");
8429
13.3k
    components.username_end += 2;
8430
13.3k
  }
8431
8432
14.2k
  size_t password_delimiter = input.find(':');
8433
8434
  // Check if input contains both username and password by checking the
8435
  // delimiter: ":" A typical input that contains authority would be "user:pass"
8436
14.2k
  if (password_delimiter != std::string_view::npos) {
8437
    // Insert both username and password
8438
116
    std::string_view username = input.substr(0, password_delimiter);
8439
116
    std::string_view password = input.substr(password_delimiter + 1);
8440
8441
116
    buffer.insert(components.protocol_end + diff, username);
8442
116
    diff += uint32_t(username.size());
8443
116
    buffer.insert(components.protocol_end + diff, ":");
8444
116
    components.username_end = components.protocol_end + diff;
8445
116
    buffer.insert(components.protocol_end + diff + 1, password);
8446
116
    diff += uint32_t(password.size()) + 1;
8447
14.1k
  } else if (!input.empty()) {
8448
    // Insert only username
8449
144
    buffer.insert(components.protocol_end + diff, input);
8450
144
    components.username_end =
8451
144
        components.protocol_end + diff + uint32_t(input.size());
8452
144
    diff += uint32_t(input.size());
8453
144
  }
8454
8455
14.2k
  components.host_start += diff;
8456
8457
14.2k
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
8458
0
    buffer.insert(components.host_start, "@");
8459
0
    diff++;
8460
0
  }
8461
14.2k
  components.host_end += diff;
8462
14.2k
  components.pathname_start += diff;
8463
14.2k
  if (components.search_start != url_components::omitted) {
8464
0
    components.search_start += diff;
8465
0
  }
8466
14.2k
  if (components.hash_start != url_components::omitted) {
8467
0
    components.hash_start += diff;
8468
0
  }
8469
14.2k
}
8470
8471
44.7k
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
8472
44.7k
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
8473
44.7k
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
8474
44.7k
          " bytes] components.hash_start = ", components.hash_start);
8475
44.7k
  ADA_ASSERT_TRUE(validate());
8476
44.7k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8477
44.7k
  if (components.hash_start != url_components::omitted) {
8478
499
    buffer.resize(components.hash_start);
8479
499
  }
8480
44.7k
  components.hash_start = uint32_t(buffer.size());
8481
44.7k
  buffer += "#";
8482
44.7k
  bool encoding_required = unicode::percent_encode<true>(
8483
44.7k
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
8484
  // When encoding_required is false, then buffer is left unchanged, and percent
8485
  // encoding was not deemed required.
8486
44.7k
  if (!encoding_required) {
8487
34.3k
    buffer.append(input);
8488
34.3k
  }
8489
44.7k
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
8490
44.7k
          buffer, "' [", buffer.size(), " bytes]");
8491
44.7k
  ADA_ASSERT_TRUE(validate());
8492
44.7k
}
8493
8494
ada_really_inline uint32_t url_aggregator::replace_and_resize(
8495
201k
    uint32_t start, uint32_t end, std::string_view input) {
8496
201k
  uint32_t current_length = end - start;
8497
201k
  uint32_t input_size = uint32_t(input.size());
8498
201k
  uint32_t new_difference = input_size - current_length;
8499
8500
201k
  if (current_length == 0) {
8501
143k
    buffer.insert(start, input);
8502
143k
  } else if (input_size == current_length) {
8503
2.50k
    if (input_size != 0) {
8504
2.50k
      std::memmove(buffer.data() + start, input.data(), input_size);
8505
2.50k
    }
8506
55.1k
  } else if (input_size < current_length) {
8507
24.7k
    buffer.erase(start, current_length - input_size);
8508
24.7k
    buffer.replace(start, input_size, input);
8509
30.4k
  } else {
8510
30.4k
    buffer.replace(start, current_length, input.substr(0, current_length));
8511
30.4k
    buffer.insert(start + current_length, input.substr(current_length));
8512
30.4k
  }
8513
8514
201k
  return new_difference;
8515
201k
}
8516
8517
88.4k
inline void url_aggregator::update_base_hostname(const std::string_view input) {
8518
88.4k
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
8519
88.4k
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
8520
88.4k
  ADA_ASSERT_TRUE(validate());
8521
88.4k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8522
8523
  // This next line is required for when parsing a URL like `foo://`
8524
88.4k
  add_authority_slashes_if_needed();
8525
8526
88.4k
  bool has_credentials = components.protocol_end + 2 < components.host_start;
8527
88.4k
  uint32_t new_difference =
8528
88.4k
      replace_and_resize(components.host_start, components.host_end, input);
8529
8530
88.4k
  if (has_credentials) {
8531
39.3k
    buffer.insert(components.host_start, "@");
8532
39.3k
    new_difference++;
8533
39.3k
  }
8534
88.4k
  components.host_end += new_difference;
8535
88.4k
  components.pathname_start += new_difference;
8536
88.4k
  if (components.search_start != url_components::omitted) {
8537
4.28k
    components.search_start += new_difference;
8538
4.28k
  }
8539
88.4k
  if (components.hash_start != url_components::omitted) {
8540
4.28k
    components.hash_start += new_difference;
8541
4.28k
  }
8542
88.4k
  ADA_ASSERT_TRUE(validate());
8543
88.4k
}
8544
8545
[[nodiscard]] ada_really_inline uint32_t
8546
138k
url_aggregator::get_pathname_length() const noexcept {
8547
138k
  ada_log("url_aggregator::get_pathname_length");
8548
138k
  uint32_t ending_index = uint32_t(buffer.size());
8549
138k
  if (components.search_start != url_components::omitted) {
8550
24.4k
    ending_index = components.search_start;
8551
113k
  } else if (components.hash_start != url_components::omitted) {
8552
1.37k
    ending_index = components.hash_start;
8553
1.37k
  }
8554
138k
  return ending_index - components.pathname_start;
8555
138k
}
8556
8557
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
8558
42.6k
    const noexcept {
8559
42.6k
  return buffer.size() == components.pathname_start;
8560
42.6k
}
8561
8562
9.08k
inline void url_aggregator::update_base_search(std::string_view input) {
8563
9.08k
  ada_log("url_aggregator::update_base_search ", input);
8564
9.08k
  ADA_ASSERT_TRUE(validate());
8565
9.08k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8566
9.08k
  if (input.empty()) {
8567
0
    clear_search();
8568
0
    return;
8569
0
  }
8570
8571
9.08k
  if (input[0] == '?') {
8572
9.08k
    input.remove_prefix(1);
8573
9.08k
  }
8574
8575
9.08k
  if (components.hash_start == url_components::omitted) {
8576
9.08k
    if (components.search_start == url_components::omitted) {
8577
9.08k
      components.search_start = uint32_t(buffer.size());
8578
9.08k
      buffer += "?";
8579
9.08k
    } else {
8580
0
      buffer.resize(components.search_start + 1);
8581
0
    }
8582
8583
9.08k
    buffer.append(input);
8584
9.08k
  } else if (components.search_start != url_components::omitted) {
8585
0
    const uint32_t difference = replace_and_resize(
8586
0
        components.search_start + 1, components.hash_start, input);
8587
0
    components.hash_start += difference;
8588
0
  } else {
8589
0
    components.search_start = components.hash_start;
8590
0
    buffer.insert(components.search_start, input.size() + 1, '?');
8591
0
    if (!input.empty()) {
8592
0
      std::memmove(buffer.data() + components.search_start + 1, input.data(),
8593
0
                   input.size());
8594
0
    }
8595
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8596
0
  }
8597
8598
9.08k
  ADA_ASSERT_TRUE(validate());
8599
9.08k
}
8600
8601
inline void url_aggregator::update_base_search(
8602
72.6k
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8603
72.6k
  ada_log("url_aggregator::update_base_search ", input,
8604
72.6k
          " with encoding parameter ", to_string(), "\n", to_diagram());
8605
72.6k
  ADA_ASSERT_TRUE(validate());
8606
72.6k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8607
8608
72.6k
  if (components.hash_start == url_components::omitted) {
8609
70.9k
    if (components.search_start == url_components::omitted) {
8610
51.7k
      components.search_start = uint32_t(buffer.size());
8611
51.7k
      buffer += "?";
8612
51.7k
    } else {
8613
19.1k
      buffer.resize(components.search_start + 1);
8614
19.1k
    }
8615
8616
70.9k
    bool encoding_required =
8617
70.9k
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8618
    // When encoding_required is false, then buffer is left unchanged, and
8619
    // percent encoding was not deemed required.
8620
70.9k
    if (!encoding_required) {
8621
53.2k
      buffer.append(input);
8622
53.2k
    }
8623
70.9k
  } else {
8624
1.64k
    const size_t idx =
8625
1.64k
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8626
1.64k
    std::string encoded;
8627
1.64k
    std::string_view replacement = input;
8628
1.64k
    if (idx != input.size()) {
8629
247
      encoded =
8630
247
          ada::unicode::percent_encode(input, query_percent_encode_set, idx);
8631
247
      replacement = encoded;
8632
247
    }
8633
8634
1.64k
    if (components.search_start != url_components::omitted) {
8635
1.61k
      const uint32_t difference = replace_and_resize(
8636
1.61k
          components.search_start + 1, components.hash_start, replacement);
8637
1.61k
      components.hash_start += difference;
8638
1.61k
    } else {
8639
31
      components.search_start = components.hash_start;
8640
31
      buffer.insert(components.search_start, replacement.size() + 1, '?');
8641
31
      if (!replacement.empty()) {
8642
20
        std::memmove(buffer.data() + components.search_start + 1,
8643
20
                     replacement.data(), replacement.size());
8644
20
      }
8645
31
      components.hash_start +=
8646
31
          uint32_t(replacement.size() + 1);  // Do not forget `?`
8647
31
    }
8648
1.64k
  }
8649
8650
72.6k
  ADA_ASSERT_TRUE(validate());
8651
72.6k
}
8652
8653
87.0k
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8654
87.0k
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8655
87.0k
          " bytes] \n", to_diagram());
8656
87.0k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8657
87.0k
  ADA_ASSERT_TRUE(validate());
8658
8659
87.0k
  const bool begins_with_dashdash = input.starts_with("//");
8660
87.0k
  if (!begins_with_dashdash && has_dash_dot()) {
8661
    // We must delete the ./
8662
42
    delete_dash_dot();
8663
42
  }
8664
8665
87.0k
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8666
266
      !has_dash_dot()) {
8667
    // If url's host is null, url does not have an opaque path, url's path's
8668
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8669
    // output.
8670
216
    buffer.insert(components.pathname_start, "/.");
8671
216
    components.pathname_start += 2;
8672
216
    if (components.search_start != url_components::omitted) {
8673
0
      components.search_start += 2;
8674
0
    }
8675
216
    if (components.hash_start != url_components::omitted) {
8676
0
      components.hash_start += 2;
8677
0
    }
8678
216
  }
8679
8680
87.0k
  uint32_t difference = replace_and_resize(
8681
87.0k
      components.pathname_start,
8682
87.0k
      components.pathname_start + get_pathname_length(), input);
8683
87.0k
  if (components.search_start != url_components::omitted) {
8684
1.46k
    components.search_start += difference;
8685
1.46k
  }
8686
87.0k
  if (components.hash_start != url_components::omitted) {
8687
1.50k
    components.hash_start += difference;
8688
1.50k
  }
8689
87.0k
  ADA_ASSERT_TRUE(validate());
8690
87.0k
}
8691
8692
26
inline void url_aggregator::append_base_pathname(const std::string_view input) {
8693
26
  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
8694
26
          "\n", to_diagram());
8695
26
  ADA_ASSERT_TRUE(validate());
8696
26
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8697
#if ADA_DEVELOPMENT_CHECKS
8698
  // computing the expected password.
8699
  std::string path_expected(get_pathname());
8700
  path_expected.append(input);
8701
#endif  // ADA_DEVELOPMENT_CHECKS
8702
26
  uint32_t ending_index = uint32_t(buffer.size());
8703
26
  if (components.search_start != url_components::omitted) {
8704
0
    ending_index = components.search_start;
8705
26
  } else if (components.hash_start != url_components::omitted) {
8706
0
    ending_index = components.hash_start;
8707
0
  }
8708
26
  buffer.insert(ending_index, input);
8709
8710
26
  if (components.search_start != url_components::omitted) {
8711
0
    components.search_start += uint32_t(input.size());
8712
0
  }
8713
26
  if (components.hash_start != url_components::omitted) {
8714
0
    components.hash_start += uint32_t(input.size());
8715
0
  }
8716
#if ADA_DEVELOPMENT_CHECKS
8717
  std::string path_after = std::string(get_pathname());
8718
  ADA_ASSERT_EQUAL(
8719
      path_expected, path_after,
8720
      "append_base_pathname problem after inserting " + std::string(input));
8721
#endif  // ADA_DEVELOPMENT_CHECKS
8722
26
  ADA_ASSERT_TRUE(validate());
8723
26
}
8724
8725
23.9k
inline void url_aggregator::update_base_username(const std::string_view input) {
8726
23.9k
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8727
23.9k
          "\n", to_diagram());
8728
23.9k
  ADA_ASSERT_TRUE(validate());
8729
23.9k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8730
8731
23.9k
  add_authority_slashes_if_needed();
8732
8733
23.9k
  bool has_password = has_non_empty_password();
8734
23.9k
  bool host_starts_with_at = buffer.size() > components.host_start &&
8735
23.9k
                             buffer[components.host_start] == '@';
8736
23.9k
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8737
23.9k
                                     components.username_end, input);
8738
8739
23.9k
  components.username_end += diff;
8740
23.9k
  components.host_start += diff;
8741
8742
23.9k
  if (!input.empty() && !host_starts_with_at) {
8743
15.9k
    buffer.insert(components.host_start, "@");
8744
15.9k
    diff++;
8745
15.9k
  } else if (input.empty() && host_starts_with_at && !has_password) {
8746
    // Input is empty, there is no password, and we need to remove "@" from
8747
    // hostname
8748
1.31k
    buffer.erase(components.host_start, 1);
8749
1.31k
    diff--;
8750
1.31k
  }
8751
8752
23.9k
  components.host_end += diff;
8753
23.9k
  components.pathname_start += diff;
8754
23.9k
  if (components.search_start != url_components::omitted) {
8755
1.71k
    components.search_start += diff;
8756
1.71k
  }
8757
23.9k
  if (components.hash_start != url_components::omitted) {
8758
1.72k
    components.hash_start += diff;
8759
1.72k
  }
8760
23.9k
  ADA_ASSERT_TRUE(validate());
8761
23.9k
}
8762
8763
40.8k
inline void url_aggregator::append_base_username(const std::string_view input) {
8764
40.8k
  ada_log("url_aggregator::append_base_username ", input);
8765
40.8k
  ADA_ASSERT_TRUE(validate());
8766
40.8k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8767
#if ADA_DEVELOPMENT_CHECKS
8768
  // computing the expected password.
8769
  std::string username_expected(get_username());
8770
  username_expected.append(input);
8771
#endif  // ADA_DEVELOPMENT_CHECKS
8772
40.8k
  add_authority_slashes_if_needed();
8773
8774
  // If input is empty, do nothing.
8775
40.8k
  if (input.empty()) {
8776
8.32k
    return;
8777
8.32k
  }
8778
8779
32.5k
  uint32_t difference = uint32_t(input.size());
8780
32.5k
  buffer.insert(components.username_end, input);
8781
32.5k
  components.username_end += difference;
8782
32.5k
  components.host_start += difference;
8783
8784
32.5k
  if (buffer[components.host_start] != '@' &&
8785
22.3k
      components.host_start != components.host_end) {
8786
22.3k
    buffer.insert(components.host_start, "@");
8787
22.3k
    difference++;
8788
22.3k
  }
8789
8790
32.5k
  components.host_end += difference;
8791
32.5k
  components.pathname_start += difference;
8792
32.5k
  if (components.search_start != url_components::omitted) {
8793
0
    components.search_start += difference;
8794
0
  }
8795
32.5k
  if (components.hash_start != url_components::omitted) {
8796
0
    components.hash_start += difference;
8797
0
  }
8798
#if ADA_DEVELOPMENT_CHECKS
8799
  std::string username_after(get_username());
8800
  ADA_ASSERT_EQUAL(
8801
      username_expected, username_after,
8802
      "append_base_username problem after inserting " + std::string(input));
8803
#endif  // ADA_DEVELOPMENT_CHECKS
8804
32.5k
  ADA_ASSERT_TRUE(validate());
8805
32.5k
}
8806
8807
4.62k
constexpr void url_aggregator::clear_password() {
8808
4.62k
  ada_log("url_aggregator::clear_password ", to_string());
8809
4.62k
  ADA_ASSERT_TRUE(validate());
8810
4.62k
  if (!has_password()) {
8811
3.20k
    return;
8812
3.20k
  }
8813
8814
1.41k
  uint32_t diff = components.host_start - components.username_end;
8815
1.41k
  buffer.erase(components.username_end, diff);
8816
1.41k
  components.host_start -= diff;
8817
1.41k
  components.host_end -= diff;
8818
1.41k
  components.pathname_start -= diff;
8819
1.41k
  if (components.search_start != url_components::omitted) {
8820
1.39k
    components.search_start -= diff;
8821
1.39k
  }
8822
1.41k
  if (components.hash_start != url_components::omitted) {
8823
1.39k
    components.hash_start -= diff;
8824
1.39k
  }
8825
1.41k
}
8826
8827
20.9k
inline void url_aggregator::update_base_password(const std::string_view input) {
8828
20.9k
  ada_log("url_aggregator::update_base_password ", input);
8829
20.9k
  ADA_ASSERT_TRUE(validate());
8830
20.9k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8831
8832
20.9k
  add_authority_slashes_if_needed();
8833
8834
  // TODO: Optimization opportunity. Merge the following removal functions.
8835
20.9k
  if (input.empty()) {
8836
4.62k
    clear_password();
8837
8838
    // Remove username too, if it is empty.
8839
4.62k
    if (!has_non_empty_username()) {
8840
3.21k
      update_base_username("");
8841
3.21k
    }
8842
8843
4.62k
    return;
8844
4.62k
  }
8845
8846
16.3k
  bool password_exists = has_password();
8847
16.3k
  uint32_t difference = uint32_t(input.size());
8848
8849
16.3k
  if (password_exists) {
8850
333
    difference = replace_and_resize(components.username_end + 1,
8851
333
                                    components.host_start, input);
8852
15.9k
  } else {
8853
15.9k
    buffer.insert(components.username_end, input.size() + 1, ':');
8854
15.9k
    std::memmove(buffer.data() + components.username_end + 1, input.data(),
8855
15.9k
                 input.size());
8856
15.9k
    difference++;
8857
15.9k
  }
8858
16.3k
  components.host_start += difference;
8859
8860
  // The following line is required to add "@" to hostname. When updating
8861
  // password if hostname does not start with "@", it is "update_base_password"s
8862
  // responsibility to set it.
8863
16.3k
  if (buffer[components.host_start] != '@') {
8864
11
    buffer.insert(components.host_start, "@");
8865
11
    difference++;
8866
11
  }
8867
8868
16.3k
  components.host_end += difference;
8869
16.3k
  components.pathname_start += difference;
8870
16.3k
  if (components.search_start != url_components::omitted) {
8871
346
    components.search_start += difference;
8872
346
  }
8873
16.3k
  if (components.hash_start != url_components::omitted) {
8874
352
    components.hash_start += difference;
8875
352
  }
8876
16.3k
  ADA_ASSERT_TRUE(validate());
8877
16.3k
}
8878
8879
32.9k
inline void url_aggregator::append_base_password(const std::string_view input) {
8880
32.9k
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8881
32.9k
          "\n", to_diagram());
8882
32.9k
  ADA_ASSERT_TRUE(validate());
8883
32.9k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8884
#if ADA_DEVELOPMENT_CHECKS
8885
  // computing the expected password.
8886
  std::string password_expected = std::string(get_password());
8887
  password_expected.append(input);
8888
#endif  // ADA_DEVELOPMENT_CHECKS
8889
32.9k
  add_authority_slashes_if_needed();
8890
8891
  // If input is empty, do nothing.
8892
32.9k
  if (input.empty()) {
8893
4.67k
    return;
8894
4.67k
  }
8895
8896
28.2k
  uint32_t difference = uint32_t(input.size());
8897
28.2k
  if (has_password()) {
8898
6.85k
    buffer.insert(components.host_start, input);
8899
21.4k
  } else {
8900
21.4k
    difference++;  // Increment for ":"
8901
21.4k
    buffer.insert(components.username_end, ":");
8902
21.4k
    buffer.insert(components.username_end + 1, input);
8903
21.4k
  }
8904
28.2k
  components.host_start += difference;
8905
8906
  // The following line is required to add "@" to hostname. When updating
8907
  // password if hostname does not start with "@", it is "append_base_password"s
8908
  // responsibility to set it.
8909
28.2k
  if (buffer[components.host_start] != '@') {
8910
685
    buffer.insert(components.host_start, "@");
8911
685
    difference++;
8912
685
  }
8913
8914
28.2k
  components.host_end += difference;
8915
28.2k
  components.pathname_start += difference;
8916
28.2k
  if (components.search_start != url_components::omitted) {
8917
0
    components.search_start += difference;
8918
0
  }
8919
28.2k
  if (components.hash_start != url_components::omitted) {
8920
0
    components.hash_start += difference;
8921
0
  }
8922
#if ADA_DEVELOPMENT_CHECKS
8923
  std::string password_after(get_password());
8924
  ADA_ASSERT_EQUAL(
8925
      password_expected, password_after,
8926
      "append_base_password problem after inserting " + std::string(input));
8927
#endif  // ADA_DEVELOPMENT_CHECKS
8928
28.2k
  ADA_ASSERT_TRUE(validate());
8929
28.2k
}
8930
8931
38.4k
inline void url_aggregator::update_base_port(uint32_t input) {
8932
38.4k
  ada_log("url_aggregator::update_base_port");
8933
38.4k
  ADA_ASSERT_TRUE(validate());
8934
38.4k
  if (input == url_components::omitted) {
8935
14.1k
    clear_port();
8936
14.1k
    return;
8937
14.1k
  }
8938
  // calling std::to_string(input.value()) is unfortunate given that the port
8939
  // value is probably already available as a string.
8940
24.3k
  std::string value = helpers::concat(":", std::to_string(input));
8941
24.3k
  uint32_t difference = uint32_t(value.size());
8942
8943
24.3k
  if (components.port != url_components::omitted) {
8944
242
    difference -= components.pathname_start - components.host_end;
8945
242
    buffer.erase(components.host_end,
8946
242
                 components.pathname_start - components.host_end);
8947
242
  }
8948
8949
24.3k
  buffer.insert(components.host_end, value);
8950
24.3k
  components.pathname_start += difference;
8951
24.3k
  if (components.search_start != url_components::omitted) {
8952
1.26k
    components.search_start += difference;
8953
1.26k
  }
8954
24.3k
  if (components.hash_start != url_components::omitted) {
8955
1.26k
    components.hash_start += difference;
8956
1.26k
  }
8957
24.3k
  components.port = input;
8958
24.3k
  ADA_ASSERT_TRUE(validate());
8959
24.3k
}
8960
8961
42.0k
inline void url_aggregator::clear_port() {
8962
42.0k
  ada_log("url_aggregator::clear_port");
8963
42.0k
  ADA_ASSERT_TRUE(validate());
8964
42.0k
  if (components.port == url_components::omitted) {
8965
38.6k
    return;
8966
38.6k
  }
8967
3.39k
  uint32_t length = components.pathname_start - components.host_end;
8968
3.39k
  buffer.erase(components.host_end, length);
8969
3.39k
  components.pathname_start -= length;
8970
3.39k
  if (components.search_start != url_components::omitted) {
8971
3.36k
    components.search_start -= length;
8972
3.36k
  }
8973
3.39k
  if (components.hash_start != url_components::omitted) {
8974
3.36k
    components.hash_start -= length;
8975
3.36k
  }
8976
3.39k
  components.port = url_components::omitted;
8977
3.39k
  ADA_ASSERT_TRUE(validate());
8978
3.39k
}
8979
8980
14.2k
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8981
14.2k
  ada_log("url_aggregator::retrieve_base_port");
8982
14.2k
  return components.port;
8983
14.2k
}
8984
8985
32.5k
inline void url_aggregator::clear_search() {
8986
32.5k
  ada_log("url_aggregator::clear_search");
8987
32.5k
  ADA_ASSERT_TRUE(validate());
8988
32.5k
  if (components.search_start == url_components::omitted) {
8989
8.72k
    return;
8990
8.72k
  }
8991
8992
23.7k
  if (components.hash_start == url_components::omitted) {
8993
6.61k
    buffer.resize(components.search_start);
8994
17.1k
  } else {
8995
17.1k
    buffer.erase(components.search_start,
8996
17.1k
                 components.hash_start - components.search_start);
8997
17.1k
    components.hash_start = components.search_start;
8998
17.1k
  }
8999
9000
23.7k
  components.search_start = url_components::omitted;
9001
9002
#if ADA_DEVELOPMENT_CHECKS
9003
  ADA_ASSERT_EQUAL(get_search(), "",
9004
                   "search should have been cleared on buffer=" + buffer +
9005
                       " with " + components.to_string() + "\n" + to_diagram());
9006
#endif
9007
23.7k
  ADA_ASSERT_TRUE(validate());
9008
23.7k
}
9009
9010
22.6k
inline void url_aggregator::clear_hash() {
9011
22.6k
  ada_log("url_aggregator::clear_hash");
9012
22.6k
  ADA_ASSERT_TRUE(validate());
9013
22.6k
  if (components.hash_start == url_components::omitted) {
9014
3.82k
    return;
9015
3.82k
  }
9016
18.8k
  buffer.resize(components.hash_start);
9017
18.8k
  components.hash_start = url_components::omitted;
9018
9019
#if ADA_DEVELOPMENT_CHECKS
9020
  ADA_ASSERT_EQUAL(get_hash(), "",
9021
                   "hash should have been cleared on buffer=" + buffer +
9022
                       " with " + components.to_string() + "\n" + to_diagram());
9023
#endif
9024
18.8k
  ADA_ASSERT_TRUE(validate());
9025
18.8k
}
9026
9027
21.8k
constexpr void url_aggregator::clear_pathname() {
9028
21.8k
  ada_log("url_aggregator::clear_pathname");
9029
21.8k
  ADA_ASSERT_TRUE(validate());
9030
21.8k
  uint32_t ending_index = uint32_t(buffer.size());
9031
21.8k
  if (components.search_start != url_components::omitted) {
9032
1.46k
    ending_index = components.search_start;
9033
20.3k
  } else if (components.hash_start != url_components::omitted) {
9034
55
    ending_index = components.hash_start;
9035
55
  }
9036
21.8k
  uint32_t pathname_length = ending_index - components.pathname_start;
9037
21.8k
  buffer.erase(components.pathname_start, pathname_length);
9038
21.8k
  uint32_t difference = pathname_length;
9039
21.8k
  if (components.pathname_start == components.host_end + 2 &&
9040
798
      buffer[components.host_end] == '/' &&
9041
0
      buffer[components.host_end + 1] == '.') {
9042
0
    components.pathname_start -= 2;
9043
0
    buffer.erase(components.host_end, 2);
9044
0
    difference += 2;
9045
0
  }
9046
21.8k
  if (components.search_start != url_components::omitted) {
9047
1.46k
    components.search_start -= difference;
9048
1.46k
  }
9049
21.8k
  if (components.hash_start != url_components::omitted) {
9050
1.50k
    components.hash_start -= difference;
9051
1.50k
  }
9052
21.8k
  ada_log("url_aggregator::clear_pathname completed, running checks...");
9053
#if ADA_DEVELOPMENT_CHECKS
9054
  ADA_ASSERT_EQUAL(get_pathname(), "",
9055
                   "pathname should have been cleared on buffer=" + buffer +
9056
                       " with " + components.to_string() + "\n" + to_diagram());
9057
#endif
9058
21.8k
  ADA_ASSERT_TRUE(validate());
9059
21.8k
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
9060
21.8k
}
9061
9062
222
constexpr void url_aggregator::clear_hostname() {
9063
222
  ada_log("url_aggregator::clear_hostname");
9064
222
  ADA_ASSERT_TRUE(validate());
9065
222
  if (!has_authority()) {
9066
0
    return;
9067
0
  }
9068
222
  ADA_ASSERT_TRUE(has_authority());
9069
9070
222
  uint32_t hostname_length = components.host_end - components.host_start;
9071
222
  uint32_t start = components.host_start;
9072
9073
  // If hostname starts with "@", we should not remove that character.
9074
222
  if (hostname_length > 0 && buffer[start] == '@') {
9075
0
    start++;
9076
0
    hostname_length--;
9077
0
  }
9078
222
  buffer.erase(start, hostname_length);
9079
222
  components.host_end = start;
9080
222
  components.pathname_start -= hostname_length;
9081
222
  if (components.search_start != url_components::omitted) {
9082
124
    components.search_start -= hostname_length;
9083
124
  }
9084
222
  if (components.hash_start != url_components::omitted) {
9085
124
    components.hash_start -= hostname_length;
9086
124
  }
9087
#if ADA_DEVELOPMENT_CHECKS
9088
  ADA_ASSERT_EQUAL(get_hostname(), "",
9089
                   "hostname should have been cleared on buffer=" + buffer +
9090
                       " with " + components.to_string() + "\n" + to_diagram());
9091
#endif
9092
222
  ADA_ASSERT_TRUE(has_authority());
9093
222
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
9094
222
                   "hostname should have been cleared on buffer=" + buffer +
9095
222
                       " with " + components.to_string() + "\n" + to_diagram());
9096
222
  ADA_ASSERT_TRUE(validate());
9097
222
}
9098
9099
71.3k
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
9100
71.3k
  ada_log("url_aggregator::has_hash");
9101
71.3k
  return components.hash_start != url_components::omitted;
9102
71.3k
}
9103
9104
86.6k
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
9105
86.6k
  ada_log("url_aggregator::has_search");
9106
86.6k
  return components.search_start != url_components::omitted;
9107
86.6k
}
9108
9109
109k
constexpr bool url_aggregator::has_credentials() const noexcept {
9110
109k
  ada_log("url_aggregator::has_credentials");
9111
109k
  return has_non_empty_username() || has_non_empty_password();
9112
109k
}
9113
9114
73.3k
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
9115
73.3k
  ada_log("url_aggregator::cannot_have_credentials_or_port");
9116
73.3k
  return type == ada::scheme::type::FILE ||
9117
70.0k
         components.host_start == components.host_end;
9118
73.3k
}
9119
9120
[[nodiscard]] ada_really_inline const ada::url_components&
9121
65.4k
url_aggregator::get_components() const noexcept {
9122
65.4k
  return components;
9123
65.4k
}
9124
9125
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
9126
386k
    const noexcept {
9127
386k
  ada_log("url_aggregator::has_authority");
9128
  // Performance: instead of doing this potentially expensive check, we could
9129
  // have a boolean in the struct.
9130
386k
  return components.protocol_end + 2 <= components.host_start &&
9131
315k
         buffer[components.protocol_end] == '/' &&
9132
315k
         buffer[components.protocol_end + 1] == '/';
9133
386k
}
9134
9135
207k
inline void ada::url_aggregator::add_authority_slashes_if_needed() {
9136
207k
  ada_log("url_aggregator::add_authority_slashes_if_needed");
9137
207k
  ADA_ASSERT_TRUE(validate());
9138
  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
9139
  // to insert
9140
  // `//` initially to the buffer, since it depends on the hostname existence.
9141
207k
  if (has_authority()) {
9142
155k
    return;
9143
155k
  }
9144
  // Performance: the common case is components.protocol_end == buffer.size()
9145
  // Optimization opportunity: in many cases, the "//" is part of the input and
9146
  // the insert could be fused with another insert.
9147
51.9k
  buffer.insert(components.protocol_end, "//");
9148
51.9k
  components.username_end += 2;
9149
51.9k
  components.host_start += 2;
9150
51.9k
  components.host_end += 2;
9151
51.9k
  components.pathname_start += 2;
9152
51.9k
  if (components.search_start != url_components::omitted) {
9153
0
    components.search_start += 2;
9154
0
  }
9155
51.9k
  if (components.hash_start != url_components::omitted) {
9156
0
    components.hash_start += 2;
9157
0
  }
9158
51.9k
  ADA_ASSERT_TRUE(validate());
9159
51.9k
}
9160
9161
114k
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
9162
114k
  buffer.reserve(capacity);
9163
114k
}
9164
9165
240k
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
9166
240k
  ada_log("url_aggregator::has_non_empty_username");
9167
240k
  return components.protocol_end + 2 < components.username_end;
9168
240k
}
9169
9170
226k
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
9171
226k
  ada_log("url_aggregator::has_non_empty_password");
9172
226k
  return components.host_start > components.username_end;
9173
226k
}
9174
9175
100k
constexpr bool url_aggregator::has_password() const noexcept {
9176
100k
  ada_log("url_aggregator::has_password");
9177
  // This function does not care about the length of the password
9178
100k
  return components.host_start > components.username_end &&
9179
24.8k
         buffer[components.username_end] == ':';
9180
100k
}
9181
9182
51.1k
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
9183
51.1k
  if (!has_hostname()) {
9184
5.96k
    return false;
9185
5.96k
  }
9186
45.1k
  if (components.host_start == components.host_end) {
9187
1.41k
    return true;
9188
1.41k
  }
9189
43.7k
  if (components.host_end > components.host_start + 1) {
9190
38.8k
    return false;
9191
38.8k
  }
9192
4.89k
  return components.username_end != components.host_start;
9193
43.7k
}
9194
9195
174k
constexpr bool url_aggregator::has_hostname() const noexcept {
9196
174k
  return has_authority();
9197
174k
}
9198
9199
71.3k
constexpr bool url_aggregator::has_port() const noexcept {
9200
71.3k
  ada_log("url_aggregator::has_port");
9201
  // A URL cannot have a username/password/port if its host is null or the empty
9202
  // string, or its scheme is "file".
9203
71.3k
  return has_hostname() && components.pathname_start != components.host_end;
9204
71.3k
}
9205
9206
101k
[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
9207
  // If url's host is null, url does not have an opaque path, url's path's size
9208
  // is greater than 1, and url's path[0] is the empty string, then append
9209
  // U+002F (/) followed by U+002E (.) to output.
9210
101k
  ada_log("url_aggregator::has_dash_dot");
9211
#if ADA_DEVELOPMENT_CHECKS
9212
  // If pathname_start and host_end are exactly two characters apart, then we
9213
  // either have a one-digit port such as http://test.com:5?param=1 or else we
9214
  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
9215
  if (components.pathname_start == components.host_end + 2) {
9216
    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
9217
                     buffer[components.host_end + 1] == '.') ||
9218
                    (buffer[components.host_end] == ':' &&
9219
                     checkers::is_digit(buffer[components.host_end + 1])));
9220
  }
9221
  if (components.pathname_start == components.host_end + 2 &&
9222
      buffer[components.host_end] == '/' &&
9223
      buffer[components.host_end + 1] == '.') {
9224
    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
9225
    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
9226
    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
9227
  }
9228
#endif
9229
  // Performance: it should be uncommon for components.pathname_start ==
9230
  // components.host_end + 2 to be true. So we put this check first in the
9231
  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
9232
  // more expensive, but should be uncommon.
9233
101k
  return components.pathname_start == components.host_end + 2 &&
9234
1.83k
         !has_opaque_path && buffer[components.host_end] == '/' &&
9235
92
         buffer[components.host_end + 1] == '.';
9236
101k
}
9237
9238
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
9239
262k
    const noexcept ada_lifetime_bound {
9240
262k
  ada_log("url_aggregator::get_href");
9241
262k
  return buffer;
9242
262k
}
9243
9244
27.3k
[[nodiscard]] constexpr size_t url_aggregator::get_href_size() const noexcept {
9245
27.3k
  return buffer.size();
9246
27.3k
}
9247
9248
ada_really_inline size_t
9249
28.1k
url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
9250
28.1k
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
9251
28.1k
  if (!view.empty() && view[0] == '-') {
9252
35
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
9253
35
    is_valid = false;
9254
35
    return 0;
9255
35
  }
9256
28.0k
  uint16_t parsed_port{};
9257
28.0k
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
9258
28.0k
  if (r.ec == std::errc::result_out_of_range) {
9259
299
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
9260
299
    is_valid = false;
9261
299
    return 0;
9262
299
  }
9263
27.7k
  ada_log("parse_port: ", parsed_port);
9264
27.7k
  const size_t consumed = size_t(r.ptr - view.data());
9265
27.7k
  ada_log("parse_port: consumed ", consumed);
9266
27.7k
  if (check_trailing_content) {
9267
25.4k
    is_valid &=
9268
25.4k
        (consumed == view.size() || view[consumed] == '/' ||
9269
907
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
9270
25.4k
  }
9271
27.7k
  ada_log("parse_port: is_valid = ", is_valid);
9272
27.7k
  if (is_valid) {
9273
27.3k
    ada_log("parse_port", r.ec == std::errc());
9274
    // scheme_default_port can return 0, and we should allow 0 as a base port.
9275
27.3k
    auto default_port = scheme_default_port();
9276
27.3k
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
9277
27.2k
                         (default_port != parsed_port);
9278
27.3k
    if (r.ec == std::errc() && is_port_valid) {
9279
24.1k
      update_base_port(parsed_port);
9280
24.1k
    } else {
9281
3.15k
      clear_port();
9282
3.15k
    }
9283
27.3k
  }
9284
27.7k
  return consumed;
9285
28.0k
}
9286
9287
8.09k
constexpr void url_aggregator::set_protocol_as_file() {
9288
8.09k
  ada_log("url_aggregator::set_protocol_as_file ");
9289
8.09k
  ADA_ASSERT_TRUE(validate());
9290
8.09k
  type = ada::scheme::type::FILE;
9291
  // next line could overflow but unsigned arithmetic has well-defined
9292
  // overflows.
9293
8.09k
  uint32_t new_difference = 5 - components.protocol_end;
9294
9295
8.09k
  if (buffer.empty()) {
9296
1.88k
    buffer.append("file:");
9297
6.20k
  } else {
9298
6.20k
    buffer.erase(0, components.protocol_end);
9299
6.20k
    buffer.insert(0, "file:");
9300
6.20k
  }
9301
8.09k
  components.protocol_end = 5;
9302
9303
  // Update the rest of the components.
9304
8.09k
  components.username_end += new_difference;
9305
8.09k
  components.host_start += new_difference;
9306
8.09k
  components.host_end += new_difference;
9307
8.09k
  components.pathname_start += new_difference;
9308
8.09k
  if (components.search_start != url_components::omitted) {
9309
0
    components.search_start += new_difference;
9310
0
  }
9311
8.09k
  if (components.hash_start != url_components::omitted) {
9312
0
    components.hash_start += new_difference;
9313
0
  }
9314
8.09k
  ADA_ASSERT_TRUE(validate());
9315
8.09k
}
9316
9317
125k
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
9318
125k
  if (!is_valid) {
9319
0
    return true;
9320
0
  }
9321
125k
  if (!components.check_offset_consistency()) {
9322
0
    ada_log("url_aggregator::validate inconsistent components \n",
9323
0
            to_diagram());
9324
0
    return false;
9325
0
  }
9326
  // We have a credible components struct, but let us investivate more
9327
  // carefully:
9328
  /**
9329
   * https://user:pass@example.com:1234/foo/bar?baz#quux
9330
   *       |     |    |          | ^^^^|       |   |
9331
   *       |     |    |          | |   |       |   `----- hash_start
9332
   *       |     |    |          | |   |       `--------- search_start
9333
   *       |     |    |          | |   `----------------- pathname_start
9334
   *       |     |    |          | `--------------------- port
9335
   *       |     |    |          `----------------------- host_end
9336
   *       |     |    `---------------------------------- host_start
9337
   *       |     `--------------------------------------- username_end
9338
   *       `--------------------------------------------- protocol_end
9339
   */
9340
125k
  if (components.protocol_end == url_components::omitted) {
9341
0
    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
9342
0
    return false;
9343
0
  }
9344
125k
  if (components.username_end == url_components::omitted) {
9345
0
    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
9346
0
    return false;
9347
0
  }
9348
125k
  if (components.host_start == url_components::omitted) {
9349
0
    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
9350
0
    return false;
9351
0
  }
9352
125k
  if (components.host_end == url_components::omitted) {
9353
0
    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
9354
0
    return false;
9355
0
  }
9356
125k
  if (components.pathname_start == url_components::omitted) {
9357
0
    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
9358
0
    return false;
9359
0
  }
9360
9361
125k
  if (components.protocol_end > buffer.size()) {
9362
0
    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
9363
0
    return false;
9364
0
  }
9365
125k
  if (components.username_end > buffer.size()) {
9366
0
    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
9367
0
    return false;
9368
0
  }
9369
125k
  if (components.host_start > buffer.size()) {
9370
0
    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
9371
0
    return false;
9372
0
  }
9373
125k
  if (components.host_end > buffer.size()) {
9374
0
    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
9375
0
    return false;
9376
0
  }
9377
125k
  if (components.pathname_start > buffer.size()) {
9378
0
    ada_log("url_aggregator::validate pathname_start overflow \n",
9379
0
            to_diagram());
9380
0
    return false;
9381
0
  }
9382
9383
125k
  if (components.protocol_end > 0) {
9384
125k
    if (buffer[components.protocol_end - 1] != ':') {
9385
0
      ada_log(
9386
0
          "url_aggregator::validate missing : at the end of the protocol \n",
9387
0
          to_diagram());
9388
0
      return false;
9389
0
    }
9390
125k
  }
9391
9392
125k
  if (components.username_end != buffer.size() &&
9393
124k
      components.username_end > components.protocol_end + 2) {
9394
50.3k
    if (buffer[components.username_end] != ':' &&
9395
2.56k
        buffer[components.username_end] != '@') {
9396
0
      ada_log(
9397
0
          "url_aggregator::validate missing : or @ at the end of the username "
9398
0
          "\n",
9399
0
          to_diagram());
9400
0
      return false;
9401
0
    }
9402
50.3k
  }
9403
9404
125k
  if (components.host_start != buffer.size()) {
9405
124k
    if (components.host_start > components.username_end) {
9406
48.6k
      if (buffer[components.host_start] != '@') {
9407
0
        ada_log(
9408
0
            "url_aggregator::validate missing @ at the end of the password \n",
9409
0
            to_diagram());
9410
0
        return false;
9411
0
      }
9412
75.4k
    } else if (components.host_start == components.username_end &&
9413
75.4k
               components.host_end > components.host_start) {
9414
65.2k
      if (components.host_start == components.protocol_end + 2) {
9415
62.6k
        if (buffer[components.protocol_end] != '/' ||
9416
62.6k
            buffer[components.protocol_end + 1] != '/') {
9417
0
          ada_log(
9418
0
              "url_aggregator::validate missing // between protocol and host "
9419
0
              "\n",
9420
0
              to_diagram());
9421
0
          return false;
9422
0
        }
9423
62.6k
      } else {
9424
2.56k
        if (components.host_start > components.protocol_end &&
9425
2.56k
            buffer[components.host_start] != '@') {
9426
0
          ada_log(
9427
0
              "url_aggregator::validate missing @ at the end of the username "
9428
0
              "\n",
9429
0
              to_diagram());
9430
0
          return false;
9431
0
        }
9432
2.56k
      }
9433
65.2k
    } else {
9434
10.2k
      if (components.host_end != components.host_start) {
9435
0
        ada_log("url_aggregator::validate expected omitted host \n",
9436
0
                to_diagram());
9437
0
        return false;
9438
0
      }
9439
10.2k
    }
9440
124k
  }
9441
125k
  if (components.host_end != buffer.size() &&
9442
123k
      components.pathname_start > components.host_end) {
9443
38.3k
    if (components.pathname_start == components.host_end + 2 &&
9444
3.14k
        buffer[components.host_end] == '/' &&
9445
86
        buffer[components.host_end + 1] == '.') {
9446
86
      if (components.pathname_start + 1 >= buffer.size() ||
9447
86
          buffer[components.pathname_start] != '/' ||
9448
86
          buffer[components.pathname_start + 1] != '/') {
9449
0
        ada_log(
9450
0
            "url_aggregator::validate expected the path to begin with // \n",
9451
0
            to_diagram());
9452
0
        return false;
9453
0
      }
9454
38.2k
    } else if (buffer[components.host_end] != ':') {
9455
0
      ada_log("url_aggregator::validate missing : at the port \n",
9456
0
              to_diagram());
9457
0
      return false;
9458
0
    }
9459
38.3k
  }
9460
125k
  if (components.pathname_start != buffer.size() &&
9461
123k
      components.pathname_start < components.search_start &&
9462
123k
      components.pathname_start < components.hash_start && !has_opaque_path) {
9463
117k
    if (buffer[components.pathname_start] != '/') {
9464
0
      ada_log("url_aggregator::validate missing / at the path \n",
9465
0
              to_diagram());
9466
0
      return false;
9467
0
    }
9468
117k
  }
9469
125k
  if (components.search_start != url_components::omitted) {
9470
86.1k
    if (buffer[components.search_start] != '?') {
9471
0
      ada_log("url_aggregator::validate missing ? at the search \n",
9472
0
              to_diagram());
9473
0
      return false;
9474
0
    }
9475
86.1k
  }
9476
125k
  if (components.hash_start != url_components::omitted) {
9477
63.1k
    if (buffer[components.hash_start] != '#') {
9478
0
      ada_log("url_aggregator::validate missing # at the hash \n",
9479
0
              to_diagram());
9480
0
      return false;
9481
0
    }
9482
63.1k
  }
9483
9484
125k
  return true;
9485
125k
}
9486
9487
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() const
9488
194k
    ada_lifetime_bound {
9489
194k
  ada_log("url_aggregator::get_pathname pathname_start = ",
9490
194k
          components.pathname_start, " buffer.size() = ", buffer.size(),
9491
194k
          " components.search_start = ", components.search_start,
9492
194k
          " components.hash_start = ", components.hash_start);
9493
194k
  auto ending_index = uint32_t(buffer.size());
9494
194k
  if (components.search_start != url_components::omitted) {
9495
58.5k
    ending_index = components.search_start;
9496
135k
  } else if (components.hash_start != url_components::omitted) {
9497
3.57k
    ending_index = components.hash_start;
9498
3.57k
  }
9499
194k
  return helpers::substring(buffer, components.pathname_start, ending_index);
9500
194k
}
9501
9502
inline std::ostream& operator<<(std::ostream& out,
9503
0
                                const ada::url_aggregator& u) {
9504
0
  return out << u.to_string();
9505
0
}
9506
9507
16.0k
void url_aggregator::update_host_to_base_host(const std::string_view input) {
9508
16.0k
  ada_log("url_aggregator::update_host_to_base_host ", input);
9509
16.0k
  ADA_ASSERT_TRUE(validate());
9510
16.0k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
9511
16.0k
  if (type != ada::scheme::type::FILE) {
9512
    // Let host be the result of host parsing host_view with url is not special.
9513
14.2k
    if (input.empty() && !is_special()) {
9514
983
      if (has_hostname()) {
9515
98
        clear_hostname();
9516
885
      } else if (has_dash_dot()) {
9517
0
        add_authority_slashes_if_needed();
9518
0
        delete_dash_dot();
9519
0
      }
9520
983
      return;
9521
983
    }
9522
14.2k
  }
9523
15.0k
  update_base_hostname(input);
9524
15.0k
  ADA_ASSERT_TRUE(validate());
9525
15.0k
  return;
9526
16.0k
}
9527
}  // namespace ada
9528
9529
#endif  // ADA_URL_AGGREGATOR_INL_H
9530
/* end file include/ada/url_aggregator-inl.h */
9531
/* begin file include/ada/url_search_params.h */
9532
/**
9533
 * @file url_search_params.h
9534
 * @brief URL query string parameter manipulation.
9535
 *
9536
 * This file provides the `url_search_params` class for parsing, manipulating,
9537
 * and serializing URL query strings. It implements the URLSearchParams API
9538
 * from the WHATWG URL Standard.
9539
 *
9540
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9541
 */
9542
#ifndef ADA_URL_SEARCH_PARAMS_H
9543
#define ADA_URL_SEARCH_PARAMS_H
9544
9545
#include <cstddef>
9546
#include <optional>
9547
#include <string>
9548
#include <string_view>
9549
#include <utility>
9550
#include <vector>
9551
9552
namespace ada {
9553
9554
/**
9555
 * @brief Iterator types for url_search_params iteration.
9556
 */
9557
enum class url_search_params_iter_type {
9558
  KEYS,    /**< Iterate over parameter keys only */
9559
  VALUES,  /**< Iterate over parameter values only */
9560
  ENTRIES, /**< Iterate over key-value pairs */
9561
};
9562
9563
template <typename T, url_search_params_iter_type Type>
9564
struct url_search_params_iter;
9565
9566
/** Type alias for a key-value pair of string views. */
9567
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
9568
9569
/** Iterator over search parameter keys. */
9570
using url_search_params_keys_iter =
9571
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
9572
/** Iterator over search parameter values. */
9573
using url_search_params_values_iter =
9574
    url_search_params_iter<std::string_view,
9575
                           url_search_params_iter_type::VALUES>;
9576
/** Iterator over search parameter key-value pairs. */
9577
using url_search_params_entries_iter =
9578
    url_search_params_iter<key_value_view_pair,
9579
                           url_search_params_iter_type::ENTRIES>;
9580
9581
/**
9582
 * @brief Class for parsing and manipulating URL query strings.
9583
 *
9584
 * The `url_search_params` class provides methods to parse, modify, and
9585
 * serialize URL query parameters (the part after '?' in a URL). It handles
9586
 * percent-encoding and decoding automatically.
9587
 *
9588
 * All string inputs must be valid UTF-8. The caller is responsible for
9589
 * ensuring UTF-8 validity.
9590
 *
9591
 * Construction and `reset` refuse query strings longer than
9592
 * `get_max_input_length()` (the object is left empty). Individual `append` /
9593
 * `set` calls are not length-capped.
9594
 *
9595
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9596
 */
9597
struct url_search_params {
9598
  url_search_params() = default;
9599
9600
  /**
9601
   * Constructs url_search_params by parsing a query string.
9602
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9603
   *        If longer than `get_max_input_length()`, the object stays empty.
9604
   */
9605
40.3k
  explicit url_search_params(const std::string_view input) {
9606
40.3k
    initialize(input);
9607
40.3k
  }
9608
9609
  url_search_params(const url_search_params& u) = default;
9610
0
  url_search_params(url_search_params&& u) noexcept = default;
9611
  url_search_params& operator=(url_search_params&& u) noexcept = default;
9612
  url_search_params& operator=(const url_search_params& u) = default;
9613
40.3k
  ~url_search_params() = default;
9614
9615
  /**
9616
   * Returns the number of key-value pairs.
9617
   * @return The total count of parameters.
9618
   */
9619
  [[nodiscard]] inline size_t size() const noexcept;
9620
9621
  /**
9622
   * Appends a new key-value pair to the parameter list.
9623
   * @param key The parameter name (must be valid UTF-8).
9624
   * @param value The parameter value (must be valid UTF-8).
9625
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9626
   */
9627
  inline void append(std::string_view key, std::string_view value);
9628
9629
  /**
9630
   * Removes all pairs with the given key.
9631
   * @param key The parameter name to remove.
9632
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9633
   */
9634
  inline void remove(std::string_view key);
9635
9636
  /**
9637
   * Removes all pairs with the given key and value.
9638
   * @param key The parameter name.
9639
   * @param value The parameter value to match.
9640
   */
9641
  inline void remove(std::string_view key, std::string_view value);
9642
9643
  /**
9644
   * Returns the value of the first pair with the given key.
9645
   * @param key The parameter name to search for.
9646
   * @return The value if found, or std::nullopt if not present.
9647
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9648
   */
9649
  inline std::optional<std::string_view> get(std::string_view key);
9650
9651
  /**
9652
   * Returns all values for pairs with the given key.
9653
   * @param key The parameter name to search for.
9654
   * @return A vector of all matching values (may be empty).
9655
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9656
   */
9657
  inline std::vector<std::string> get_all(std::string_view key);
9658
9659
  /**
9660
   * Checks if any pair has the given key.
9661
   * @param key The parameter name to search for.
9662
   * @return `true` if at least one pair has this key.
9663
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9664
   */
9665
  inline bool has(std::string_view key) noexcept;
9666
9667
  /**
9668
   * Checks if any pair matches the given key and value.
9669
   * @param key The parameter name to search for.
9670
   * @param value The parameter value to match.
9671
   * @return `true` if a matching pair exists.
9672
   */
9673
  inline bool has(std::string_view key, std::string_view value) noexcept;
9674
9675
  /**
9676
   * Sets a parameter value, replacing any existing pairs with the same key.
9677
   * @param key The parameter name (must be valid UTF-8).
9678
   * @param value The parameter value (must be valid UTF-8).
9679
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9680
   */
9681
  inline void set(std::string_view key, std::string_view value);
9682
9683
  /**
9684
   * Sorts all key-value pairs by their keys using code unit comparison.
9685
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9686
   */
9687
  inline void sort();
9688
9689
  /**
9690
   * Serializes the parameters to a query string (without leading '?').
9691
   * @return The percent-encoded query string.
9692
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9693
   */
9694
  inline std::string to_string() const;
9695
9696
  /**
9697
   * Returns an iterator over all parameter keys.
9698
   * Keys may repeat if there are duplicate parameters.
9699
   * @return An iterator yielding string_view keys.
9700
   * @note The iterator is invalidated if this object is modified.
9701
   */
9702
  inline url_search_params_keys_iter get_keys();
9703
9704
  /**
9705
   * Returns an iterator over all parameter values.
9706
   * @return An iterator yielding string_view values.
9707
   * @note The iterator is invalidated if this object is modified.
9708
   */
9709
  inline url_search_params_values_iter get_values();
9710
9711
  /**
9712
   * Returns an iterator over all key-value pairs.
9713
   * @return An iterator yielding key-value pair views.
9714
   * @note The iterator is invalidated if this object is modified.
9715
   */
9716
  inline url_search_params_entries_iter get_entries();
9717
9718
  /**
9719
   * C++ style conventional iterator support. const only because we
9720
   * do not really want the params to be modified via the iterator.
9721
   */
9722
0
  inline auto begin() const { return params.begin(); }
9723
0
  inline auto end() const { return params.end(); }
9724
0
  inline auto front() const { return params.front(); }
9725
0
  inline auto back() const { return params.back(); }
9726
0
  inline auto operator[](size_t index) const { return params[index]; }
9727
9728
  /**
9729
   * @private
9730
   * Used to reset the search params to a new input.
9731
   * Used primarily for C API.
9732
   * @param input
9733
   */
9734
  void reset(std::string_view input);
9735
9736
 private:
9737
  typedef std::pair<std::string, std::string> key_value_pair;
9738
  std::vector<key_value_pair> params{};
9739
9740
  /**
9741
   * The init parameter must be valid UTF-8.
9742
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9743
   */
9744
  void initialize(std::string_view init);
9745
9746
  template <typename T, url_search_params_iter_type Type>
9747
  friend struct url_search_params_iter;
9748
};  // url_search_params
9749
9750
/**
9751
 * @brief JavaScript-style iterator for url_search_params.
9752
 *
9753
 * Provides a `next()` method that returns successive values until exhausted.
9754
 * This matches the iterator pattern used in the Web Platform.
9755
 *
9756
 * @tparam T The type of value returned by the iterator.
9757
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9758
 *
9759
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9760
 */
9761
template <typename T, url_search_params_iter_type Type>
9762
struct url_search_params_iter {
9763
0
  inline url_search_params_iter() : params(EMPTY) {}
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::url_search_params_iter()
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::url_search_params_iter()
Unexecuted instantiation: ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::url_search_params_iter()
9764
  url_search_params_iter(const url_search_params_iter& u) = default;
9765
  url_search_params_iter(url_search_params_iter&& u) noexcept = default;
9766
  url_search_params_iter& operator=(url_search_params_iter&& u) noexcept =
9767
      default;
9768
  url_search_params_iter& operator=(const url_search_params_iter& u) = default;
9769
  ~url_search_params_iter() = default;
9770
9771
  /**
9772
   * Returns the next value in the iteration sequence.
9773
   * @return The next value, or std::nullopt if iteration is complete.
9774
   */
9775
  inline std::optional<T> next();
9776
9777
  /**
9778
   * Checks if more values are available.
9779
   * @return `true` if `next()` will return a value, `false` if exhausted.
9780
   */
9781
  inline bool has_next() const;
9782
9783
 private:
9784
  static url_search_params EMPTY;
9785
0
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::url_search_params_iter(ada::url_search_params&)
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::url_search_params_iter(ada::url_search_params&)
Unexecuted instantiation: ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::url_search_params_iter(ada::url_search_params&)
9786
9787
  url_search_params& params;
9788
  size_t pos = 0;
9789
9790
  friend struct url_search_params;
9791
};
9792
9793
}  // namespace ada
9794
#endif
9795
/* end file include/ada/url_search_params.h */
9796
/* begin file include/ada/url_search_params-inl.h */
9797
/**
9798
 * @file url_search_params-inl.h
9799
 * @brief Inline declarations for the URL Search Params
9800
 */
9801
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9802
#define ADA_URL_SEARCH_PARAMS_INL_H
9803
9804
9805
#include <algorithm>
9806
#include <cstddef>
9807
#include <cstdint>
9808
#include <iterator>
9809
#include <optional>
9810
#include <ranges>
9811
#include <string>
9812
#include <string_view>
9813
#include <vector>
9814
9815
namespace ada {
9816
9817
// Declared in implementation.h; used here as a DoS bound on untrusted query
9818
// strings (ada.h includes both headers).
9819
uint32_t get_max_input_length();
9820
9821
// A default, empty url_search_params for use with empty iterators.
9822
template <typename T, ada::url_search_params_iter_type Type>
9823
url_search_params url_search_params_iter<T, Type>::EMPTY;
9824
9825
0
inline void url_search_params::reset(std::string_view input) {
9826
0
  params.clear();
9827
0
  initialize(input);
9828
0
}
9829
9830
40.3k
inline void url_search_params::initialize(std::string_view input) {
9831
40.3k
  if (!input.empty() && input.front() == '?') {
9832
292
    input.remove_prefix(1);
9833
292
  }
9834
40.3k
  if (input.empty()) {
9835
3.75k
    return;
9836
3.75k
  }
9837
  // Refuse overlong query strings (same process-wide cap as URL parsing).
9838
36.6k
  if (input.size() > get_max_input_length()) {
9839
0
    return;
9840
0
  }
9841
9842
36.6k
  params.reserve(size_t(std::count(input.begin(), input.end(), '&')) + 1);
9843
9844
57.1k
  auto process_key_value = [&](const std::string_view current) {
9845
57.1k
    const auto equal = current.find('=');
9846
57.1k
    if (equal == std::string_view::npos) {
9847
17.5k
      params.emplace_back(unicode::form_urlencoded_decode(current), "");
9848
39.6k
    } else {
9849
39.6k
      params.emplace_back(
9850
39.6k
          unicode::form_urlencoded_decode(current.substr(0, equal)),
9851
39.6k
          unicode::form_urlencoded_decode(current.substr(equal + 1)));
9852
39.6k
    }
9853
57.1k
  };
9854
9855
58.5k
  while (!input.empty()) {
9856
58.4k
    const auto ampersand_index = input.find('&');
9857
9858
58.4k
    if (ampersand_index == std::string_view::npos) {
9859
36.5k
      if (!input.empty()) {
9860
36.5k
        process_key_value(input);
9861
36.5k
      }
9862
36.5k
      break;
9863
36.5k
    } else if (ampersand_index != 0) {
9864
20.6k
      process_key_value(input.substr(0, ampersand_index));
9865
20.6k
    }
9866
9867
21.8k
    input.remove_prefix(ampersand_index + 1);
9868
21.8k
  }
9869
36.6k
}
9870
9871
inline void url_search_params::append(const std::string_view key,
9872
20.1k
                                      const std::string_view value) {
9873
20.1k
  params.emplace_back(key, value);
9874
20.1k
}
9875
9876
0
inline size_t url_search_params::size() const noexcept { return params.size(); }
9877
9878
inline std::optional<std::string_view> url_search_params::get(
9879
0
    const std::string_view key) {
9880
0
  auto entry = std::ranges::find_if(
9881
0
      params, [&key](const auto& param) { return param.first == key; });
9882
9883
0
  if (entry == params.end()) {
9884
0
    return std::nullopt;
9885
0
  }
9886
9887
0
  return entry->second;
9888
0
}
9889
9890
inline std::vector<std::string> url_search_params::get_all(
9891
0
    const std::string_view key) {
9892
0
  std::vector<std::string> out{};
9893
9894
0
  for (auto& param : params) {
9895
0
    if (param.first == key) {
9896
0
      out.emplace_back(param.second);
9897
0
    }
9898
0
  }
9899
9900
0
  return out;
9901
0
}
9902
9903
0
inline bool url_search_params::has(const std::string_view key) noexcept {
9904
0
  auto entry = std::ranges::find_if(
9905
0
      params, [&key](const auto& param) { return param.first == key; });
9906
0
  return entry != params.end();
9907
0
}
9908
9909
inline bool url_search_params::has(std::string_view key,
9910
0
                                   std::string_view value) noexcept {
9911
0
  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
9912
0
    return param.first == key && param.second == value;
9913
0
  });
9914
0
  return entry != params.end();
9915
0
}
9916
9917
40.3k
inline std::string url_search_params::to_string() const {
9918
40.3k
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9919
40.3k
  std::string out{};
9920
117k
  for (size_t i = 0; i < params.size(); i++) {
9921
77.3k
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9922
77.3k
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9923
9924
    // Performance optimization: Move this inside percent_encode.
9925
77.3k
    std::ranges::replace(key, ' ', '+');
9926
77.3k
    std::ranges::replace(value, ' ', '+');
9927
9928
77.3k
    if (i != 0) {
9929
36.9k
      out += "&";
9930
36.9k
    }
9931
77.3k
    out.append(key);
9932
77.3k
    out += "=";
9933
77.3k
    out.append(value);
9934
77.3k
  }
9935
40.3k
  return out;
9936
40.3k
}
9937
9938
inline void url_search_params::set(const std::string_view key,
9939
0
                                   const std::string_view value) {
9940
0
  const auto find = [&key](const auto& param) { return param.first == key; };
9941
9942
0
  auto it = std::ranges::find_if(params, find);
9943
9944
0
  if (it == params.end()) {
9945
0
    params.emplace_back(key, value);
9946
0
  } else {
9947
0
    it->second = value;
9948
0
    params.erase(std::remove_if(std::next(it), params.end(), find),
9949
0
                 params.end());
9950
0
  }
9951
0
}
9952
9953
0
inline void url_search_params::remove(const std::string_view key) {
9954
0
  std::erase_if(params,
9955
0
                [&key](const auto& param) { return param.first == key; });
9956
0
}
9957
9958
inline void url_search_params::remove(const std::string_view key,
9959
0
                                      const std::string_view value) {
9960
0
  std::erase_if(params, [&key, &value](const auto& param) {
9961
0
    return param.first == key && param.second == value;
9962
0
  });
9963
0
}
9964
9965
0
inline void url_search_params::sort() {
9966
  // Keys are expected to be valid UTF-8, but percent_decode can produce
9967
  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
9968
0
  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
9969
0
                                      const key_value_pair& rhs) {
9970
0
    size_t i = 0, j = 0;
9971
0
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9972
0
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9973
0
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9974
0
      uint32_t codePoint1 = 0, codePoint2 = 0;
9975
9976
0
      if (low_surrogate1 != 0) {
9977
0
        codePoint1 = low_surrogate1;
9978
0
        low_surrogate1 = 0;
9979
0
      } else {
9980
0
        uint8_t c1 = uint8_t(lhs.first[i]);
9981
0
        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
9982
0
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9983
0
          i += 2;
9984
0
        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
9985
0
          codePoint1 = ((c1 & 0x0F) << 12) |
9986
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9987
0
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9988
0
          i += 3;
9989
0
        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
9990
0
          codePoint1 = ((c1 & 0x07) << 18) |
9991
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9992
0
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9993
0
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9994
0
          i += 4;
9995
9996
0
          codePoint1 -= 0x10000;
9997
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9998
0
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9999
0
          codePoint1 = high_surrogate;
10000
0
        } else {
10001
          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10002
0
          codePoint1 = c1;
10003
0
          i++;
10004
0
        }
10005
0
      }
10006
10007
0
      if (low_surrogate2 != 0) {
10008
0
        codePoint2 = low_surrogate2;
10009
0
        low_surrogate2 = 0;
10010
0
      } else {
10011
0
        uint8_t c2 = uint8_t(rhs.first[j]);
10012
0
        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
10013
0
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
10014
0
          j += 2;
10015
0
        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
10016
0
          codePoint2 = ((c2 & 0x0F) << 12) |
10017
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
10018
0
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
10019
0
          j += 3;
10020
0
        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
10021
0
          codePoint2 = ((c2 & 0x07) << 18) |
10022
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
10023
0
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
10024
0
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
10025
0
          j += 4;
10026
0
          codePoint2 -= 0x10000;
10027
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
10028
0
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
10029
0
          codePoint2 = high_surrogate;
10030
0
        } else {
10031
          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10032
0
          codePoint2 = c2;
10033
0
          j++;
10034
0
        }
10035
0
      }
10036
10037
0
      if (codePoint1 != codePoint2) {
10038
0
        return (codePoint1 < codePoint2);
10039
0
      }
10040
0
    }
10041
0
    return (j < rhs.first.size() || low_surrogate2 != 0);
10042
0
  });
10043
0
}
10044
10045
0
inline url_search_params_keys_iter url_search_params::get_keys() {
10046
0
  return url_search_params_keys_iter(*this);
10047
0
}
10048
10049
/**
10050
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
10051
 */
10052
0
inline url_search_params_values_iter url_search_params::get_values() {
10053
0
  return url_search_params_values_iter(*this);
10054
0
}
10055
10056
/**
10057
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
10058
 */
10059
0
inline url_search_params_entries_iter url_search_params::get_entries() {
10060
0
  return url_search_params_entries_iter(*this);
10061
0
}
10062
10063
template <typename T, url_search_params_iter_type Type>
10064
0
inline bool url_search_params_iter<T, Type>::has_next() const {
10065
0
  return pos < params.params.size();
10066
0
}
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::has_next() const
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::has_next() const
Unexecuted instantiation: ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::has_next() const
10067
10068
template <>
10069
0
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
10070
0
  if (!has_next()) {
10071
0
    return std::nullopt;
10072
0
  }
10073
0
  return params.params[pos++].first;
10074
0
}
10075
10076
template <>
10077
0
inline std::optional<std::string_view> url_search_params_values_iter::next() {
10078
0
  if (!has_next()) {
10079
0
    return std::nullopt;
10080
0
  }
10081
0
  return params.params[pos++].second;
10082
0
}
10083
10084
template <>
10085
inline std::optional<key_value_view_pair>
10086
0
url_search_params_entries_iter::next() {
10087
0
  if (!has_next()) {
10088
0
    return std::nullopt;
10089
0
  }
10090
0
  return params.params[pos++];
10091
0
}
10092
10093
}  // namespace ada
10094
10095
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
10096
/* end file include/ada/url_search_params-inl.h */
10097
10098
/* begin file include/ada/url_pattern-inl.h */
10099
/**
10100
 * @file url_pattern-inl.h
10101
 * @brief Declaration for the URLPattern inline functions.
10102
 */
10103
#ifndef ADA_URL_PATTERN_INL_H
10104
#define ADA_URL_PATTERN_INL_H
10105
10106
10107
#include <algorithm>
10108
#include <cstddef>
10109
#include <optional>
10110
#include <string>
10111
#include <string_view>
10112
#include <utility>
10113
#include <variant>
10114
#include <vector>
10115
10116
#if ADA_INCLUDE_URL_PATTERN
10117
namespace ada {
10118
10119
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
10120
0
  return protocol == other.protocol && username == other.username &&
10121
0
         password == other.password && hostname == other.hostname &&
10122
0
         port == other.port && search == other.search && hash == other.hash &&
10123
0
         pathname == other.pathname;
10124
0
}
10125
10126
inline bool url_pattern_component_result::operator==(
10127
0
    const url_pattern_component_result& other) const {
10128
0
  return input == other.input && groups == other.groups;
10129
0
}
10130
10131
template <url_pattern_regex::regex_concept regex_provider>
10132
url_pattern_component_result
10133
url_pattern_component<regex_provider>::create_component_match_result(
10134
    std::string&& input,
10135
    std::vector<std::optional<std::string>>&& exec_result) {
10136
  // Let result be a new URLPatternComponentResult.
10137
  // Set result["input"] to input.
10138
  // Let groups be a record<USVString, (USVString or undefined)>.
10139
  auto result =
10140
      url_pattern_component_result{.input = std::move(input), .groups = {}};
10141
10142
  // We explicitly start iterating from 0 even though the spec
10143
  // says we should start from 1. This case is handled by the
10144
  // std_regex_provider which removes the full match from index 0.
10145
  // Use min() to guard against potential mismatches between
10146
  // exec_result size and group_name_list size.
10147
  const size_t size = std::min(exec_result.size(), group_name_list.size());
10148
  result.groups.reserve(size);
10149
  for (size_t index = 0; index < size; index++) {
10150
    result.groups.emplace(group_name_list[index],
10151
                          std::move(exec_result[index]));
10152
  }
10153
  return result;
10154
}
10155
10156
template <url_pattern_regex::regex_concept regex_provider>
10157
std::string_view url_pattern<regex_provider>::get_protocol() const
10158
    ada_lifetime_bound {
10159
  // Return this's associated URL pattern's protocol component's pattern string.
10160
  return protocol_component.pattern;
10161
}
10162
template <url_pattern_regex::regex_concept regex_provider>
10163
std::string_view url_pattern<regex_provider>::get_username() const
10164
    ada_lifetime_bound {
10165
  // Return this's associated URL pattern's username component's pattern string.
10166
  return username_component.pattern;
10167
}
10168
template <url_pattern_regex::regex_concept regex_provider>
10169
std::string_view url_pattern<regex_provider>::get_password() const
10170
    ada_lifetime_bound {
10171
  // Return this's associated URL pattern's password component's pattern string.
10172
  return password_component.pattern;
10173
}
10174
template <url_pattern_regex::regex_concept regex_provider>
10175
std::string_view url_pattern<regex_provider>::get_hostname() const
10176
    ada_lifetime_bound {
10177
  // Return this's associated URL pattern's hostname component's pattern string.
10178
  return hostname_component.pattern;
10179
}
10180
template <url_pattern_regex::regex_concept regex_provider>
10181
std::string_view url_pattern<regex_provider>::get_port() const
10182
    ada_lifetime_bound {
10183
  // Return this's associated URL pattern's port component's pattern string.
10184
  return port_component.pattern;
10185
}
10186
template <url_pattern_regex::regex_concept regex_provider>
10187
std::string_view url_pattern<regex_provider>::get_pathname() const
10188
    ada_lifetime_bound {
10189
  // Return this's associated URL pattern's pathname component's pattern string.
10190
  return pathname_component.pattern;
10191
}
10192
template <url_pattern_regex::regex_concept regex_provider>
10193
std::string_view url_pattern<regex_provider>::get_search() const
10194
    ada_lifetime_bound {
10195
  // Return this's associated URL pattern's search component's pattern string.
10196
  return search_component.pattern;
10197
}
10198
template <url_pattern_regex::regex_concept regex_provider>
10199
std::string_view url_pattern<regex_provider>::get_hash() const
10200
    ada_lifetime_bound {
10201
  // Return this's associated URL pattern's hash component's pattern string.
10202
  return hash_component.pattern;
10203
}
10204
template <url_pattern_regex::regex_concept regex_provider>
10205
bool url_pattern<regex_provider>::ignore_case() const {
10206
  return ignore_case_;
10207
}
10208
template <url_pattern_regex::regex_concept regex_provider>
10209
bool url_pattern<regex_provider>::has_regexp_groups() const {
10210
  // If this's associated URL pattern's has regexp groups, then return true.
10211
  return protocol_component.has_regexp_groups ||
10212
         username_component.has_regexp_groups ||
10213
         password_component.has_regexp_groups ||
10214
         hostname_component.has_regexp_groups ||
10215
         port_component.has_regexp_groups ||
10216
         pathname_component.has_regexp_groups ||
10217
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
10218
}
10219
10220
0
inline bool url_pattern_part::is_regexp() const noexcept {
10221
0
  return type == url_pattern_part_type::REGEXP;
10222
0
}
10223
10224
inline std::string_view url_pattern_compile_component_options::get_delimiter()
10225
0
    const {
10226
0
  if (delimiter) {
10227
0
    return {&delimiter.value(), 1};
10228
0
  }
10229
0
  return {};
10230
0
}
10231
10232
inline std::string_view url_pattern_compile_component_options::get_prefix()
10233
0
    const {
10234
0
  if (prefix) {
10235
0
    return {&prefix.value(), 1};
10236
0
  }
10237
0
  return {};
10238
0
}
10239
10240
template <url_pattern_regex::regex_concept regex_provider>
10241
template <url_pattern_encoding_callback F>
10242
tl::expected<url_pattern_component<regex_provider>, errors>
10243
url_pattern_component<regex_provider>::compile(
10244
    std::string_view input, F& encoding_callback,
10245
    url_pattern_compile_component_options& options) {
10246
  ada_log("url_pattern_component::compile input: ", input);
10247
  // Let part list be the result of running parse a pattern string given input,
10248
  // options, and encoding callback.
10249
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
10250
                                                             encoding_callback);
10251
10252
  if (!part_list) {
10253
    ada_log("parse_pattern_string failed");
10254
    return tl::unexpected(part_list.error());
10255
  }
10256
10257
  // Detect pattern type early to potentially skip expensive regex compilation
10258
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
10259
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
10260
10261
  url_pattern_component_type component_type =
10262
      url_pattern_component_type::REGEXP;
10263
  std::string exact_match_value{};
10264
10265
  if (part_list->empty()) {
10266
    component_type = url_pattern_component_type::EMPTY;
10267
  } else if (part_list->size() == 1) {
10268
    const auto& part = (*part_list)[0];
10269
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
10270
        part.modifier == url_pattern_part_modifier::none &&
10271
        !options.ignore_case) {
10272
      component_type = url_pattern_component_type::EXACT_MATCH;
10273
      exact_match_value = part.value;
10274
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
10275
               part.modifier == url_pattern_part_modifier::none &&
10276
               part.prefix.empty() && part.suffix.empty()) {
10277
      component_type = url_pattern_component_type::FULL_WILDCARD;
10278
    }
10279
  }
10280
10281
  // For simple patterns, skip regex generation and compilation entirely
10282
  if (component_type != url_pattern_component_type::REGEXP) {
10283
    auto pattern_string =
10284
        url_pattern_helpers::generate_pattern_string(*part_list, options);
10285
    // For FULL_WILDCARD, we need the group name from
10286
    // generate_regular_expression
10287
    std::vector<std::string> name_list;
10288
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
10289
        !part_list->empty()) {
10290
      name_list.push_back((*part_list)[0].name);
10291
    }
10292
    return url_pattern_component<regex_provider>(
10293
        std::move(pattern_string), typename regex_provider::regex_type{},
10294
        std::move(name_list), has_regexp_groups, component_type,
10295
        std::move(exact_match_value));
10296
  }
10297
10298
  // Generate regex for complex patterns
10299
  auto [regular_expression_string, name_list] =
10300
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
10301
                                                                     options);
10302
  auto pattern_string =
10303
      url_pattern_helpers::generate_pattern_string(*part_list, options);
10304
10305
  std::optional<typename regex_provider::regex_type> regular_expression =
10306
      regex_provider::create_instance(regular_expression_string,
10307
                                      options.ignore_case);
10308
  if (!regular_expression) {
10309
    return tl::unexpected(errors::type_error);
10310
  }
10311
10312
  return url_pattern_component<regex_provider>(
10313
      std::move(pattern_string), std::move(*regular_expression),
10314
      std::move(name_list), has_regexp_groups, component_type,
10315
      std::move(exact_match_value));
10316
}
10317
10318
template <url_pattern_regex::regex_concept regex_provider>
10319
bool url_pattern_component<regex_provider>::fast_test(
10320
    std::string_view input) const {
10321
  // Fast path for simple patterns - avoid regex evaluation
10322
  // Using if-else for better branch prediction on common cases
10323
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10324
    return true;
10325
  }
10326
  if (type == url_pattern_component_type::EXACT_MATCH) {
10327
    return input == exact_match_value;
10328
  }
10329
  if (type == url_pattern_component_type::EMPTY) {
10330
    return input.empty();
10331
  }
10332
  // type == REGEXP
10333
  return regex_provider::regex_match(input, regexp);
10334
}
10335
10336
template <url_pattern_regex::regex_concept regex_provider>
10337
std::optional<std::vector<std::optional<std::string>>>
10338
url_pattern_component<regex_provider>::fast_match(
10339
    std::string_view input) const {
10340
  // Handle each type directly without redundant checks
10341
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10342
    // FULL_WILDCARD always matches - capture the input (even if empty)
10343
    // If there's no group name, return empty groups
10344
    if (group_name_list.empty()) {
10345
      return std::vector<std::optional<std::string>>{};
10346
    }
10347
    // Capture the matched input (including empty strings)
10348
    return std::vector<std::optional<std::string>>{std::string(input)};
10349
  }
10350
  if (type == url_pattern_component_type::EXACT_MATCH) {
10351
    if (input == exact_match_value) {
10352
      return std::vector<std::optional<std::string>>{};
10353
    }
10354
    return std::nullopt;
10355
  }
10356
  if (type == url_pattern_component_type::EMPTY) {
10357
    if (input.empty()) {
10358
      return std::vector<std::optional<std::string>>{};
10359
    }
10360
    return std::nullopt;
10361
  }
10362
  // type == REGEXP - use regex
10363
  return regex_provider::regex_search(input, regexp);
10364
}
10365
10366
template <url_pattern_regex::regex_concept regex_provider>
10367
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::exec(
10368
    const url_pattern_input& input, const std::string_view* base_url) {
10369
  // Return the result of match given this's associated URL pattern, input, and
10370
  // baseURL if given.
10371
  return match(input, base_url);
10372
}
10373
10374
template <url_pattern_regex::regex_concept regex_provider>
10375
bool url_pattern<regex_provider>::test_components(
10376
    std::string_view protocol, std::string_view username,
10377
    std::string_view password, std::string_view hostname, std::string_view port,
10378
    std::string_view pathname, std::string_view search,
10379
    std::string_view hash) const {
10380
  return protocol_component.fast_test(protocol) &&
10381
         username_component.fast_test(username) &&
10382
         password_component.fast_test(password) &&
10383
         hostname_component.fast_test(hostname) &&
10384
         port_component.fast_test(port) &&
10385
         pathname_component.fast_test(pathname) &&
10386
         search_component.fast_test(search) && hash_component.fast_test(hash);
10387
}
10388
10389
template <url_pattern_regex::regex_concept regex_provider>
10390
result<bool> url_pattern<regex_provider>::test(
10391
    const url_pattern_input& input, const std::string_view* base_url_string) {
10392
  // If input is a URLPatternInit
10393
  if (std::holds_alternative<url_pattern_init>(input)) {
10394
    if (base_url_string) {
10395
      return tl::unexpected(errors::type_error);
10396
    }
10397
10398
    std::string protocol{}, username{}, password{}, hostname{};
10399
    std::string port{}, pathname{}, search{}, hash{};
10400
10401
    auto apply_result = url_pattern_init::process(
10402
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10403
        protocol, username, password, hostname, port, pathname, search, hash);
10404
10405
    if (!apply_result) {
10406
      return false;
10407
    }
10408
10409
    std::string_view search_view = *apply_result->search;
10410
    if (search_view.starts_with("?")) {
10411
      search_view.remove_prefix(1);
10412
    }
10413
10414
    return test_components(*apply_result->protocol, *apply_result->username,
10415
                           *apply_result->password, *apply_result->hostname,
10416
                           *apply_result->port, *apply_result->pathname,
10417
                           search_view, *apply_result->hash);
10418
  }
10419
10420
  // URL string input path.
10421
  // Let baseURL be null. A default-constructed result holds a value (an
10422
  // empty URL), and a relative input would then resolve against it. Start
10423
  // from an error so "no base given" means no value.
10424
  result<url_aggregator> base_url = tl::unexpected(errors::type_error);
10425
  if (base_url_string) {
10426
    base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10427
    if (!base_url) {
10428
      return false;
10429
    }
10430
  }
10431
10432
  auto url =
10433
      ada::parse<url_aggregator>(std::get<std::string_view>(input),
10434
                                 base_url.has_value() ? &*base_url : nullptr);
10435
  if (!url) {
10436
    return false;
10437
  }
10438
10439
  // Extract components as string_view
10440
  auto protocol_view = url->get_protocol();
10441
  if (protocol_view.ends_with(":")) {
10442
    protocol_view.remove_suffix(1);
10443
  }
10444
10445
  auto search_view = url->get_search();
10446
  if (search_view.starts_with("?")) {
10447
    search_view.remove_prefix(1);
10448
  }
10449
10450
  auto hash_view = url->get_hash();
10451
  if (hash_view.starts_with("#")) {
10452
    hash_view.remove_prefix(1);
10453
  }
10454
10455
  return test_components(protocol_view, url->get_username(),
10456
                         url->get_password(), url->get_hostname(),
10457
                         url->get_port(), url->get_pathname(), search_view,
10458
                         hash_view);
10459
}
10460
10461
template <url_pattern_regex::regex_concept regex_provider>
10462
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::match(
10463
    const url_pattern_input& input, const std::string_view* base_url_string) {
10464
  std::string protocol{};
10465
  std::string username{};
10466
  std::string password{};
10467
  std::string hostname{};
10468
  std::string port{};
10469
  std::string pathname{};
10470
  std::string search{};
10471
  std::string hash{};
10472
10473
  // Let inputs be an empty list.
10474
  // Append input to inputs.
10475
  std::vector inputs{input};
10476
10477
  // If input is a URLPatternInit then:
10478
  if (std::holds_alternative<url_pattern_init>(input)) {
10479
    ada_log(
10480
        "url_pattern::match called with url_pattern_init and base_url_string=",
10481
        base_url_string);
10482
    // If baseURLString was given, throw a TypeError.
10483
    if (base_url_string) {
10484
      ada_log("failed to match because base_url_string was given");
10485
      return tl::unexpected(errors::type_error);
10486
    }
10487
10488
    // Let applyResult be the result of process a URLPatternInit given input,
10489
    // "url", protocol, username, password, hostname, port, pathname, search,
10490
    // and hash.
10491
    auto apply_result = url_pattern_init::process(
10492
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10493
        protocol, username, password, hostname, port, pathname, search, hash);
10494
10495
    // If this throws an exception, catch it, and return null.
10496
    if (!apply_result.has_value()) {
10497
      ada_log("match returned std::nullopt because process threw");
10498
      return std::nullopt;
10499
    }
10500
10501
    // Set protocol to applyResult["protocol"].
10502
    ADA_ASSERT_TRUE(apply_result->protocol.has_value());
10503
    protocol = std::move(apply_result->protocol.value());
10504
10505
    // Set username to applyResult["username"].
10506
    ADA_ASSERT_TRUE(apply_result->username.has_value());
10507
    username = std::move(apply_result->username.value());
10508
10509
    // Set password to applyResult["password"].
10510
    ADA_ASSERT_TRUE(apply_result->password.has_value());
10511
    password = std::move(apply_result->password.value());
10512
10513
    // Set hostname to applyResult["hostname"].
10514
    ADA_ASSERT_TRUE(apply_result->hostname.has_value());
10515
    hostname = std::move(apply_result->hostname.value());
10516
10517
    // Set port to applyResult["port"].
10518
    ADA_ASSERT_TRUE(apply_result->port.has_value());
10519
    port = std::move(apply_result->port.value());
10520
10521
    // Set pathname to applyResult["pathname"].
10522
    ADA_ASSERT_TRUE(apply_result->pathname.has_value());
10523
    pathname = std::move(apply_result->pathname.value());
10524
10525
    // Set search to applyResult["search"].
10526
    ADA_ASSERT_TRUE(apply_result->search.has_value());
10527
    if (apply_result->search->starts_with("?")) {
10528
      search = apply_result->search->substr(1);
10529
    } else {
10530
      search = std::move(apply_result->search.value());
10531
    }
10532
10533
    // Set hash to applyResult["hash"].
10534
    ADA_ASSERT_TRUE(apply_result->hash.has_value());
10535
    ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#"));
10536
    hash = std::move(apply_result->hash.value());
10537
  } else {
10538
    ADA_ASSERT_TRUE(std::holds_alternative<std::string_view>(input));
10539
10540
    // Let baseURL be null. A default-constructed result holds a value (an
10541
    // empty URL), and a relative input would then resolve against it. Start
10542
    // from an error so "no base given" means no value.
10543
    result<url_aggregator> base_url = tl::unexpected(errors::type_error);
10544
10545
    // If baseURLString was given, then:
10546
    if (base_url_string) {
10547
      // Let baseURL be the result of parsing baseURLString.
10548
      base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10549
10550
      // If baseURL is failure, return null.
10551
      if (!base_url) {
10552
        ada_log("match returned std::nullopt because failed to parse base_url=",
10553
                *base_url_string);
10554
        return std::nullopt;
10555
      }
10556
10557
      // Append baseURLString to inputs.
10558
      inputs.emplace_back(*base_url_string);
10559
    }
10560
10561
    url_aggregator* base_url_value =
10562
        base_url.has_value() ? &*base_url : nullptr;
10563
10564
    // Set url to the result of parsing input given baseURL.
10565
    auto url = ada::parse<url_aggregator>(std::get<std::string_view>(input),
10566
                                          base_url_value);
10567
10568
    // If url is failure, return null.
10569
    if (!url) {
10570
      ada_log("match returned std::nullopt because url failed");
10571
      return std::nullopt;
10572
    }
10573
10574
    // Set protocol to url's scheme.
10575
    // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':'
10576
    // is removed. Similar work was done on workerd:
10577
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038
10578
    protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1);
10579
    // Set username to url's username.
10580
    username = url->get_username();
10581
    // Set password to url's password.
10582
    password = url->get_password();
10583
    // Set hostname to url's host, serialized, or the empty string if the value
10584
    // is null.
10585
    hostname = url->get_hostname();
10586
    // Set port to url's port, serialized, or the empty string if the value is
10587
    // null.
10588
    port = url->get_port();
10589
    // Set pathname to the result of URL path serializing url.
10590
    pathname = url->get_pathname();
10591
    // Set search to url's query or the empty string if the value is null.
10592
    // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?'
10593
    // is removed. Similar work was done on workerd:
10594
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232
10595
    if (url->has_search()) {
10596
      auto view = url->get_search();
10597
      search = view.starts_with("?") ? url->get_search().substr(1) : view;
10598
    }
10599
    // Set hash to url's fragment or the empty string if the value is null.
10600
    // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is
10601
    // removed. Similar work was done on workerd:
10602
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242
10603
    if (url->has_hash()) {
10604
      auto view = url->get_hash();
10605
      hash = view.starts_with("#") ? url->get_hash().substr(1) : view;
10606
    }
10607
  }
10608
10609
  // Use fast_match which skips regex for simple patterns (EMPTY, EXACT_MATCH,
10610
  // FULL_WILDCARD) and only falls back to regex for complex REGEXP patterns.
10611
10612
  // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol
10613
  // component's regular expression, protocol).
10614
  auto protocol_exec_result = protocol_component.fast_match(protocol);
10615
  if (!protocol_exec_result) {
10616
    return std::nullopt;
10617
  }
10618
10619
  // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username
10620
  // component's regular expression, username).
10621
  auto username_exec_result = username_component.fast_match(username);
10622
  if (!username_exec_result) {
10623
    return std::nullopt;
10624
  }
10625
10626
  // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password
10627
  // component's regular expression, password).
10628
  auto password_exec_result = password_component.fast_match(password);
10629
  if (!password_exec_result) {
10630
    return std::nullopt;
10631
  }
10632
10633
  // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname
10634
  // component's regular expression, hostname).
10635
  auto hostname_exec_result = hostname_component.fast_match(hostname);
10636
  if (!hostname_exec_result) {
10637
    return std::nullopt;
10638
  }
10639
10640
  // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's
10641
  // regular expression, port).
10642
  auto port_exec_result = port_component.fast_match(port);
10643
  if (!port_exec_result) {
10644
    return std::nullopt;
10645
  }
10646
10647
  // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname
10648
  // component's regular expression, pathname).
10649
  auto pathname_exec_result = pathname_component.fast_match(pathname);
10650
  if (!pathname_exec_result) {
10651
    return std::nullopt;
10652
  }
10653
10654
  // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's
10655
  // regular expression, search).
10656
  auto search_exec_result = search_component.fast_match(search);
10657
  if (!search_exec_result) {
10658
    return std::nullopt;
10659
  }
10660
10661
  // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's
10662
  // regular expression, hash).
10663
  auto hash_exec_result = hash_component.fast_match(hash);
10664
  if (!hash_exec_result) {
10665
    return std::nullopt;
10666
  }
10667
10668
  // Let result be a new URLPatternResult.
10669
  auto result = url_pattern_result{};
10670
  // Set result["inputs"] to inputs.
10671
  result.inputs = std::move(inputs);
10672
  // Set result["protocol"] to the result of creating a component match result
10673
  // given urlPattern's protocol component, protocol, and protocolExecResult.
10674
  result.protocol = protocol_component.create_component_match_result(
10675
      std::move(protocol), std::move(*protocol_exec_result));
10676
10677
  // Set result["username"] to the result of creating a component match result
10678
  // given urlPattern's username component, username, and usernameExecResult.
10679
  result.username = username_component.create_component_match_result(
10680
      std::move(username), std::move(*username_exec_result));
10681
10682
  // Set result["password"] to the result of creating a component match result
10683
  // given urlPattern's password component, password, and passwordExecResult.
10684
  result.password = password_component.create_component_match_result(
10685
      std::move(password), std::move(*password_exec_result));
10686
10687
  // Set result["hostname"] to the result of creating a component match result
10688
  // given urlPattern's hostname component, hostname, and hostnameExecResult.
10689
  result.hostname = hostname_component.create_component_match_result(
10690
      std::move(hostname), std::move(*hostname_exec_result));
10691
10692
  // Set result["port"] to the result of creating a component match result given
10693
  // urlPattern's port component, port, and portExecResult.
10694
  result.port = port_component.create_component_match_result(
10695
      std::move(port), std::move(*port_exec_result));
10696
10697
  // Set result["pathname"] to the result of creating a component match result
10698
  // given urlPattern's pathname component, pathname, and pathnameExecResult.
10699
  result.pathname = pathname_component.create_component_match_result(
10700
      std::move(pathname), std::move(*pathname_exec_result));
10701
10702
  // Set result["search"] to the result of creating a component match result
10703
  // given urlPattern's search component, search, and searchExecResult.
10704
  result.search = search_component.create_component_match_result(
10705
      std::move(search), std::move(*search_exec_result));
10706
10707
  // Set result["hash"] to the result of creating a component match result given
10708
  // urlPattern's hash component, hash, and hashExecResult.
10709
  result.hash = hash_component.create_component_match_result(
10710
      std::move(hash), std::move(*hash_exec_result));
10711
10712
  return result;
10713
}
10714
10715
}  // namespace ada
10716
#endif  // ADA_INCLUDE_URL_PATTERN
10717
#endif
10718
/* end file include/ada/url_pattern-inl.h */
10719
/* begin file include/ada/url_pattern_helpers-inl.h */
10720
/**
10721
 * @file url_pattern_helpers-inl.h
10722
 * @brief Declaration for the URLPattern helpers.
10723
 */
10724
#ifndef ADA_URL_PATTERN_HELPERS_INL_H
10725
#define ADA_URL_PATTERN_HELPERS_INL_H
10726
10727
#include <cstddef>
10728
#include <cstdint>
10729
#include <optional>
10730
#include <string>
10731
#include <string_view>
10732
#include <utility>
10733
#include <vector>
10734
10735
10736
#if ADA_INCLUDE_URL_PATTERN
10737
namespace ada::url_pattern_helpers {
10738
#if defined(ADA_TESTING) || defined(ADA_LOGGING)
10739
0
inline std::string to_string(token_type type) {
10740
0
  switch (type) {
10741
0
    case token_type::INVALID_CHAR:
10742
0
      return "INVALID_CHAR";
10743
0
    case token_type::OPEN:
10744
0
      return "OPEN";
10745
0
    case token_type::CLOSE:
10746
0
      return "CLOSE";
10747
0
    case token_type::REGEXP:
10748
0
      return "REGEXP";
10749
0
    case token_type::NAME:
10750
0
      return "NAME";
10751
0
    case token_type::CHAR:
10752
0
      return "CHAR";
10753
0
    case token_type::ESCAPED_CHAR:
10754
0
      return "ESCAPED_CHAR";
10755
0
    case token_type::OTHER_MODIFIER:
10756
0
      return "OTHER_MODIFIER";
10757
0
    case token_type::ASTERISK:
10758
0
      return "ASTERISK";
10759
0
    case token_type::END:
10760
0
      return "END";
10761
0
    default:
10762
0
      ada::unreachable();
10763
0
  }
10764
0
}
10765
#endif  // defined(ADA_TESTING) || defined(ADA_LOGGING)
10766
10767
template <url_pattern_regex::regex_concept regex_provider>
10768
constexpr void constructor_string_parser<regex_provider>::rewind() {
10769
  // Set parser's token index to parser's component start.
10770
  token_index = component_start;
10771
  // Set parser's token increment to 0.
10772
  token_increment = 0;
10773
}
10774
10775
template <url_pattern_regex::regex_concept regex_provider>
10776
constexpr bool constructor_string_parser<regex_provider>::is_hash_prefix() {
10777
  // Return the result of running is a non-special pattern char given parser,
10778
  // parser's token index and "#".
10779
  return is_non_special_pattern_char(token_index, '#');
10780
}
10781
10782
template <url_pattern_regex::regex_concept regex_provider>
10783
constexpr bool constructor_string_parser<regex_provider>::is_search_prefix() {
10784
  // If result of running is a non-special pattern char given parser, parser's
10785
  // token index and "?" is true, then return true.
10786
  if (is_non_special_pattern_char(token_index, '?')) {
10787
    return true;
10788
  }
10789
10790
  // If parser's token list[parser's token index]'s value is not "?", then
10791
  // return false.
10792
  if (token_list[token_index].value != "?") {
10793
    return false;
10794
  }
10795
10796
  // If previous index is less than 0, then return true.
10797
  if (token_index == 0) return true;
10798
  // Let previous index be parser's token index - 1.
10799
  auto previous_index = token_index - 1;
10800
  // Let previous token be the result of running get a safe token given parser
10801
  // and previous index.
10802
  auto previous_token = get_safe_token(previous_index);
10803
  ADA_ASSERT_TRUE(previous_token);
10804
  // If any of the following are true, then return false:
10805
  // - previous token's type is "name".
10806
  // - previous token's type is "regexp".
10807
  // - previous token's type is "close".
10808
  // - previous token's type is "asterisk".
10809
  return !(previous_token->type == token_type::NAME ||
10810
           previous_token->type == token_type::REGEXP ||
10811
           previous_token->type == token_type::CLOSE ||
10812
           previous_token->type == token_type::ASTERISK);
10813
}
10814
10815
template <url_pattern_regex::regex_concept regex_provider>
10816
constexpr bool
10817
constructor_string_parser<regex_provider>::is_non_special_pattern_char(
10818
    size_t index, uint32_t value) const {
10819
  // Let token be the result of running get a safe token given parser and index.
10820
  auto token = get_safe_token(index);
10821
  ADA_ASSERT_TRUE(token);
10822
10823
  // If token's value is not value, then return false.
10824
  // TODO: Remove this once we make sure get_safe_token returns a non-empty
10825
  // string.
10826
  if (!token->value.empty() &&
10827
      static_cast<uint32_t>(token->value[0]) != value) {
10828
    return false;
10829
  }
10830
10831
  // If any of the following are true:
10832
  // - token's type is "char";
10833
  // - token's type is "escaped-char"; or
10834
  // - token's type is "invalid-char",
10835
  // - then return true.
10836
  return token->type == token_type::CHAR ||
10837
         token->type == token_type::ESCAPED_CHAR ||
10838
         token->type == token_type::INVALID_CHAR;
10839
}
10840
10841
template <url_pattern_regex::regex_concept regex_provider>
10842
constexpr const token*
10843
constructor_string_parser<regex_provider>::get_safe_token(size_t index) const {
10844
  // If index is less than parser's token list's size, then return parser's
10845
  // token list[index].
10846
  if (index < token_list.size()) [[likely]] {
10847
    return &token_list[index];
10848
  }
10849
10850
  // Assert: parser's token list's size is greater than or equal to 1.
10851
  ADA_ASSERT_TRUE(!token_list.empty());
10852
10853
  // Let token be parser's token list[last index].
10854
  // Assert: token's type is "end".
10855
  ADA_ASSERT_TRUE(token_list.back().type == token_type::END);
10856
10857
  // Return token.
10858
  return &token_list.back();
10859
}
10860
10861
template <url_pattern_regex::regex_concept regex_provider>
10862
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10863
    const {
10864
  // If parser's token list[parser's token index]'s type is "open", then return
10865
  // true.
10866
  return token_list[token_index].type == token_type::OPEN;
10867
}
10868
10869
template <url_pattern_regex::regex_concept regex_provider>
10870
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10871
    const {
10872
  // If parser's token list[parser's token index]'s type is "close", then return
10873
  // true.
10874
  return token_list[token_index].type == token_type::CLOSE;
10875
}
10876
10877
template <url_pattern_regex::regex_concept regex_provider>
10878
constexpr bool
10879
constructor_string_parser<regex_provider>::next_is_authority_slashes() const {
10880
  // If the result of running is a non-special pattern char given parser,
10881
  // parser's token index + 1, and "/" is false, then return false.
10882
  if (!is_non_special_pattern_char(token_index + 1, '/')) {
10883
    return false;
10884
  }
10885
  // If the result of running is a non-special pattern char given parser,
10886
  // parser's token index + 2, and "/" is false, then return false.
10887
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10888
    return false;
10889
  }
10890
  return true;
10891
}
10892
10893
template <url_pattern_regex::regex_concept regex_provider>
10894
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10895
    const {
10896
  // Return the result of running is a non-special pattern char given parser,
10897
  // parser's token index, and ":".
10898
  return is_non_special_pattern_char(token_index, ':');
10899
}
10900
10901
template <url_pattern_regex::regex_concept regex_provider>
10902
void constructor_string_parser<regex_provider>::change_state(State new_state,
10903
                                                             size_t skip) {
10904
  // If parser's state is not "init", not "authority", and not "done", then set
10905
  // parser's result[parser's state] to the result of running make a component
10906
  // string given parser.
10907
  if (state != State::INIT && state != State::AUTHORITY &&
10908
      state != State::DONE) {
10909
    auto value = make_component_string();
10910
    // TODO: Simplify this.
10911
    switch (state) {
10912
      case State::PROTOCOL: {
10913
        result.protocol = value;
10914
        break;
10915
      }
10916
      case State::USERNAME: {
10917
        result.username = value;
10918
        break;
10919
      }
10920
      case State::PASSWORD: {
10921
        result.password = value;
10922
        break;
10923
      }
10924
      case State::HOSTNAME: {
10925
        result.hostname = value;
10926
        break;
10927
      }
10928
      case State::PORT: {
10929
        result.port = value;
10930
        break;
10931
      }
10932
      case State::PATHNAME: {
10933
        result.pathname = value;
10934
        break;
10935
      }
10936
      case State::SEARCH: {
10937
        result.search = value;
10938
        break;
10939
      }
10940
      case State::HASH: {
10941
        result.hash = value;
10942
        break;
10943
      }
10944
      default:
10945
        ada::unreachable();
10946
    }
10947
  }
10948
10949
  // If parser's state is not "init" and new state is not "done", then:
10950
  if (state != State::INIT && new_state != State::DONE) {
10951
    // If parser's state is "protocol", "authority", "username", or "password";
10952
    // new state is "port", "pathname", "search", or "hash"; and parser's
10953
    // result["hostname"] does not exist, then set parser's result["hostname"]
10954
    // to the empty string.
10955
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10956
         state == State::USERNAME || state == State::PASSWORD) &&
10957
        (new_state == State::PORT || new_state == State::PATHNAME ||
10958
         new_state == State::SEARCH || new_state == State::HASH) &&
10959
        !result.hostname)
10960
      result.hostname = "";
10961
  }
10962
10963
  // If parser's state is "protocol", "authority", "username", "password",
10964
  // "hostname", or "port"; new state is "search" or "hash"; and parser's
10965
  // result["pathname"] does not exist, then:
10966
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10967
       state == State::USERNAME || state == State::PASSWORD ||
10968
       state == State::HOSTNAME || state == State::PORT) &&
10969
      (new_state == State::SEARCH || new_state == State::HASH) &&
10970
      !result.pathname) {
10971
    if (protocol_matches_a_special_scheme_flag) {
10972
      result.pathname = "/";
10973
    } else {
10974
      // Otherwise, set parser's result["pathname"] to the empty string.
10975
      result.pathname = "";
10976
    }
10977
  }
10978
10979
  // If parser's state is "protocol", "authority", "username", "password",
10980
  // "hostname", "port", or "pathname"; new state is "hash"; and parser's
10981
  // result["search"] does not exist, then set parser's result["search"] to
10982
  // the empty string.
10983
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10984
       state == State::USERNAME || state == State::PASSWORD ||
10985
       state == State::HOSTNAME || state == State::PORT ||
10986
       state == State::PATHNAME) &&
10987
      new_state == State::HASH && !result.search) {
10988
    result.search = "";
10989
  }
10990
10991
  // Set parser's state to new state.
10992
  state = new_state;
10993
  // Increment parser's token index by skip.
10994
  token_index += skip;
10995
  // Set parser's component start to parser's token index.
10996
  component_start = token_index;
10997
  // Set parser's token increment to 0.
10998
  token_increment = 0;
10999
}
11000
11001
template <url_pattern_regex::regex_concept regex_provider>
11002
std::string constructor_string_parser<regex_provider>::make_component_string() {
11003
  // Assert: parser's token index is less than parser's token list's size.
11004
  ADA_ASSERT_TRUE(token_index < token_list.size());
11005
11006
  // Let token be parser's token list[parser's token index].
11007
  // Let end index be token's index.
11008
  const auto end_index = token_list[token_index].index;
11009
  // Let component start token be the result of running get a safe token given
11010
  // parser and parser's component start.
11011
  const auto component_start_token = get_safe_token(component_start);
11012
  ADA_ASSERT_TRUE(component_start_token);
11013
  // Let component start input index be component start token's index.
11014
  const auto component_start_input_index = component_start_token->index;
11015
  // Return the code point substring from component start input index to end
11016
  // index within parser's input.
11017
  return std::string(input.substr(component_start_input_index,
11018
                                  end_index - component_start_input_index));
11019
}
11020
11021
template <url_pattern_regex::regex_concept regex_provider>
11022
constexpr bool
11023
constructor_string_parser<regex_provider>::is_an_identity_terminator() const {
11024
  // Return the result of running is a non-special pattern char given parser,
11025
  // parser's token index, and "@".
11026
  return is_non_special_pattern_char(token_index, '@');
11027
}
11028
11029
template <url_pattern_regex::regex_concept regex_provider>
11030
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
11031
    const {
11032
  // Return the result of running is a non-special pattern char given parser,
11033
  // parser's token index, and "/".
11034
  return is_non_special_pattern_char(token_index, '/');
11035
}
11036
11037
template <url_pattern_regex::regex_concept regex_provider>
11038
constexpr bool constructor_string_parser<regex_provider>::is_password_prefix()
11039
    const {
11040
  // Return the result of running is a non-special pattern char given parser,
11041
  // parser's token index, and ":".
11042
  return is_non_special_pattern_char(token_index, ':');
11043
}
11044
11045
template <url_pattern_regex::regex_concept regex_provider>
11046
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_open()
11047
    const {
11048
  // Return the result of running is a non-special pattern char given parser,
11049
  // parser's token index, and "[".
11050
  return is_non_special_pattern_char(token_index, '[');
11051
}
11052
11053
template <url_pattern_regex::regex_concept regex_provider>
11054
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
11055
    const {
11056
  // Return the result of running is a non-special pattern char given parser,
11057
  // parser's token index, and "]".
11058
  return is_non_special_pattern_char(token_index, ']');
11059
}
11060
11061
template <url_pattern_regex::regex_concept regex_provider>
11062
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
11063
    const {
11064
  // Return the result of running is a non-special pattern char given parser,
11065
  // parser's token index, and ":".
11066
  return is_non_special_pattern_char(token_index, ':');
11067
}
11068
11069
0
constexpr void Tokenizer::get_next_code_point() {
11070
0
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
11071
0
  ADA_ASSERT_TRUE(next_index < input.size());
11072
  // Decode the next UTF-8 code point. If malformed or truncated, mark it as
11073
  // invalid, return the offending byte as the code point, and advance by one
11074
  // to guarantee forward progress.
11075
0
  invalid_code_point = false;
11076
0
  code_point = 0;
11077
0
  size_t number_bytes = 0;
11078
0
  const size_t initial_index = next_index;
11079
0
  unsigned char first_byte = input[next_index];
11080
11081
0
  if ((first_byte & 0x80) == 0) {
11082
    // 1-byte character (ASCII)
11083
0
    next_index++;
11084
0
    code_point = first_byte;
11085
0
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
11086
0
            uint32_t(code_point));
11087
0
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
11088
0
            " input.size()=", input.size());
11089
0
    return;
11090
0
  }
11091
0
  ada_log("Tokenizer::get_next_code_point read first byte=",
11092
0
          uint32_t(first_byte));
11093
0
  if ((first_byte & 0xE0) == 0xC0) {
11094
0
    code_point = first_byte & 0x1F;
11095
0
    number_bytes = 2;
11096
0
    ada_log("Tokenizer::get_next_code_point two bytes");
11097
0
  } else if ((first_byte & 0xF0) == 0xE0) {
11098
0
    code_point = first_byte & 0x0F;
11099
0
    number_bytes = 3;
11100
0
    ada_log("Tokenizer::get_next_code_point three bytes");
11101
0
  } else if ((first_byte & 0xF8) == 0xF0) {
11102
0
    code_point = first_byte & 0x07;
11103
0
    number_bytes = 4;
11104
0
    ada_log("Tokenizer::get_next_code_point four bytes");
11105
0
  }
11106
11107
  // Invalid leading bytes that still match a multi-byte prefix.
11108
0
  if ((number_bytes == 2 && first_byte < 0xC2) ||
11109
0
      (number_bytes == 4 && first_byte > 0xF4)) {
11110
0
    invalid_code_point = true;
11111
0
    code_point = first_byte;
11112
0
    next_index = initial_index + 1;
11113
0
    return;
11114
0
  }
11115
11116
  // Invalid leading byte (e.g., continuation byte outside a sequence).
11117
0
  if (number_bytes == 0) {
11118
0
    invalid_code_point = true;
11119
0
    code_point = first_byte;
11120
0
    next_index = initial_index + 1;
11121
0
    return;
11122
0
  }
11123
11124
  // Truncated UTF-8 sequence.
11125
0
  if (number_bytes + next_index > input.size()) {
11126
0
    invalid_code_point = true;
11127
0
    code_point = first_byte;
11128
0
    next_index = initial_index + 1;
11129
0
    return;
11130
0
  }
11131
11132
0
  for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) {
11133
0
    unsigned char byte = input[i];
11134
0
    if ((byte & 0xC0) != 0x80) {
11135
0
      invalid_code_point = true;
11136
0
      code_point = first_byte;
11137
0
      next_index = initial_index + 1;
11138
0
      return;
11139
0
    }
11140
0
    ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte));
11141
0
    code_point = (code_point << 6) | (byte & 0x3F);
11142
0
  }
11143
0
  ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=",
11144
0
          uint32_t(code_point));
11145
0
  ada_log("Tokenizer::get_next_code_point next_index =", next_index,
11146
0
          " input.size()=", input.size());
11147
0
  next_index += number_bytes;
11148
0
}
11149
11150
0
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
11151
0
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
11152
0
          new_index);
11153
  // Set tokenizer's next index to index.
11154
0
  next_index = new_index;
11155
  // Run get the next code point given tokenizer.
11156
0
  get_next_code_point();
11157
0
}
11158
11159
inline void Tokenizer::add_token(token_type type, size_t next_position,
11160
0
                                 size_t value_position, size_t value_length) {
11161
0
  ada_log("Tokenizer::add_token called with type=", to_string(type),
11162
0
          " next_position=", next_position, " value_position=", value_position);
11163
0
  ADA_ASSERT_TRUE(next_position >= value_position);
11164
11165
  // Let token be a new token.
11166
  // Set token's type to type.
11167
  // Set token's index to tokenizer's index.
11168
  // Set token's value to the code point substring from value position with
11169
  // length value length within tokenizer's input.
11170
  // Append token to the back of tokenizer's token list.
11171
0
  token_list.emplace_back(type, index,
11172
0
                          input.substr(value_position, value_length));
11173
  // Set tokenizer's index to next position.
11174
0
  index = next_position;
11175
0
}
11176
11177
inline void Tokenizer::add_token_with_default_length(token_type type,
11178
                                                     size_t next_position,
11179
0
                                                     size_t value_position) {
11180
  // Let computed length be next position - value position.
11181
0
  auto computed_length = next_position - value_position;
11182
  // Run add a token given tokenizer, type, next position, value position, and
11183
  // computed length.
11184
0
  add_token(type, next_position, value_position, computed_length);
11185
0
}
11186
11187
0
inline void Tokenizer::add_token_with_defaults(token_type type) {
11188
0
  ada_log("Tokenizer::add_token_with_defaults called with type=",
11189
0
          to_string(type));
11190
  // Run add a token with default length given tokenizer, type, tokenizer's next
11191
  // index, and tokenizer's index.
11192
0
  add_token_with_default_length(type, next_index, index);
11193
0
}
11194
11195
inline ada_warn_unused std::optional<errors>
11196
Tokenizer::process_tokenizing_error(size_t next_position,
11197
0
                                    size_t value_position) {
11198
  // If tokenizer's policy is "strict", then throw a TypeError.
11199
0
  if (policy == token_policy::strict) {
11200
0
    ada_log("process_tokenizing_error failed with next_position=",
11201
0
            next_position, " value_position=", value_position);
11202
0
    return errors::type_error;
11203
0
  }
11204
  // Assert: tokenizer's policy is "lenient".
11205
0
  ADA_ASSERT_TRUE(policy == token_policy::lenient);
11206
  // Run add a token with default length given tokenizer, "invalid-char", next
11207
  // position, and value position.
11208
0
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
11209
0
                                value_position);
11210
0
  return std::nullopt;
11211
0
}
11212
11213
template <url_pattern_encoding_callback F>
11214
0
token* url_pattern_parser<F>::try_consume_modifier_token() {
11215
  // Let token be the result of running try to consume a token given parser and
11216
  // "other-modifier".
11217
0
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
11218
  // If token is not null, then return token.
11219
0
  if (token) return token;
11220
  // Set token to the result of running try to consume a token given parser and
11221
  // "asterisk".
11222
  // Return token.
11223
0
  return try_consume_token(token_type::ASTERISK);
11224
0
}
11225
11226
template <url_pattern_encoding_callback F>
11227
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
11228
0
    const token* name_token) {
11229
  // Let token be the result of running try to consume a token given parser and
11230
  // "regexp".
11231
0
  auto token = try_consume_token(token_type::REGEXP);
11232
  // If name token is null and token is null, then set token to the result of
11233
  // running try to consume a token given parser and "asterisk".
11234
0
  if (!name_token && !token) {
11235
0
    token = try_consume_token(token_type::ASTERISK);
11236
0
  }
11237
  // Return token.
11238
0
  return token;
11239
0
}
11240
11241
template <url_pattern_encoding_callback F>
11242
0
token* url_pattern_parser<F>::try_consume_token(token_type type) {
11243
0
  ada_log("url_pattern_parser::try_consume_token called with type=",
11244
0
          to_string(type));
11245
  // Assert: parser's index is less than parser's token list size.
11246
0
  ADA_ASSERT_TRUE(index < tokens.size());
11247
  // Let next token be parser's token list[parser's index].
11248
0
  auto& next_token = tokens[index];
11249
  // If next token's type is not type return null.
11250
0
  if (next_token.type != type) return nullptr;
11251
  // Increase parser's index by 1.
11252
0
  index++;
11253
  // Return next token.
11254
0
  return &next_token;
11255
0
}
11256
11257
template <url_pattern_encoding_callback F>
11258
0
std::string url_pattern_parser<F>::consume_text() {
11259
  // Let result be the empty string.
11260
0
  std::string result{};
11261
  // While true:
11262
0
  while (true) {
11263
    // Let token be the result of running try to consume a token given parser
11264
    // and "char".
11265
0
    auto token = try_consume_token(token_type::CHAR);
11266
    // If token is null, then set token to the result of running try to consume
11267
    // a token given parser and "escaped-char".
11268
0
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
11269
    // If token is null, then break.
11270
0
    if (!token) break;
11271
    // Append token's value to the end of result.
11272
0
    result.append(token->value);
11273
0
  }
11274
  // Return result.
11275
0
  return result;
11276
0
}
11277
11278
template <url_pattern_encoding_callback F>
11279
0
bool url_pattern_parser<F>::consume_required_token(token_type type) {
11280
0
  ada_log("url_pattern_parser::consume_required_token called with type=",
11281
0
          to_string(type));
11282
  // Let result be the result of running try to consume a token given parser and
11283
  // type.
11284
0
  return try_consume_token(type) != nullptr;
11285
0
}
11286
11287
template <url_pattern_encoding_callback F>
11288
std::optional<errors>
11289
0
url_pattern_parser<F>::maybe_add_part_from_the_pending_fixed_value() {
11290
  // If parser's pending fixed value is the empty string, then return.
11291
0
  if (pending_fixed_value.empty()) {
11292
0
    ada_log("pending_fixed_value is empty");
11293
0
    return std::nullopt;
11294
0
  }
11295
  // Let encoded value be the result of running parser's encoding callback given
11296
  // parser's pending fixed value.
11297
0
  auto encoded_value = encoding_callback(pending_fixed_value);
11298
0
  if (!encoded_value) {
11299
0
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
11300
0
    return encoded_value.error();
11301
0
  }
11302
  // Set parser's pending fixed value to the empty string.
11303
0
  pending_fixed_value.clear();
11304
  // Let part be a new part whose type is "fixed-text", value is encoded value,
11305
  // and modifier is "none".
11306
  // Append part to parser's part list.
11307
0
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11308
0
                     std::move(*encoded_value),
11309
0
                     url_pattern_part_modifier::none);
11310
0
  return std::nullopt;
11311
0
}
11312
11313
template <url_pattern_encoding_callback F>
11314
std::optional<errors> url_pattern_parser<F>::add_part(
11315
    std::string_view prefix, token* name_token, token* regexp_or_wildcard_token,
11316
0
    std::string_view suffix, token* modifier_token) {
11317
  // Let modifier be "none".
11318
0
  auto modifier = url_pattern_part_modifier::none;
11319
  // If modifier token is not null:
11320
0
  if (modifier_token) {
11321
    // If modifier token's value is "?" then set modifier to "optional".
11322
0
    if (modifier_token->value == "?") {
11323
0
      modifier = url_pattern_part_modifier::optional;
11324
0
    } else if (modifier_token->value == "*") {
11325
      // Otherwise if modifier token's value is "*" then set modifier to
11326
      // "zero-or-more".
11327
0
      modifier = url_pattern_part_modifier::zero_or_more;
11328
0
    } else if (modifier_token->value == "+") {
11329
      // Otherwise if modifier token's value is "+" then set modifier to
11330
      // "one-or-more".
11331
0
      modifier = url_pattern_part_modifier::one_or_more;
11332
0
    }
11333
0
  }
11334
  // If name token is null and regexp or wildcard token is null and modifier
11335
  // is "none":
11336
0
  if (!name_token && !regexp_or_wildcard_token &&
11337
0
      modifier == url_pattern_part_modifier::none) {
11338
    // Append prefix to the end of parser's pending fixed value.
11339
0
    pending_fixed_value.append(prefix);
11340
0
    return std::nullopt;
11341
0
  }
11342
  // Run maybe add a part from the pending fixed value given parser.
11343
0
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
11344
0
    return *error;
11345
0
  }
11346
  // If name token is null and regexp or wildcard token is null:
11347
0
  if (!name_token && !regexp_or_wildcard_token) {
11348
    // Assert: suffix is the empty string.
11349
0
    ADA_ASSERT_TRUE(suffix.empty());
11350
    // If prefix is the empty string, then return.
11351
0
    if (prefix.empty()) return std::nullopt;
11352
    // Let encoded value be the result of running parser's encoding callback
11353
    // given prefix.
11354
0
    auto encoded_value = encoding_callback(prefix);
11355
0
    if (!encoded_value) {
11356
0
      return encoded_value.error();
11357
0
    }
11358
    // Let part be a new part whose type is "fixed-text", value is encoded
11359
    // value, and modifier is modifier.
11360
    // Append part to parser's part list.
11361
0
    parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11362
0
                       std::move(*encoded_value), modifier);
11363
0
    return std::nullopt;
11364
0
  }
11365
  // Let regexp value be the empty string.
11366
0
  std::string regexp_value{};
11367
  // If regexp or wildcard token is null, then set regexp value to parser's
11368
  // segment wildcard regexp.
11369
0
  if (!regexp_or_wildcard_token) {
11370
0
    regexp_value = segment_wildcard_regexp;
11371
0
  } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) {
11372
    // Otherwise if regexp or wildcard token's type is "asterisk", then set
11373
    // regexp value to the full wildcard regexp value.
11374
0
    regexp_value = ".*";
11375
0
  } else {
11376
    // Otherwise set regexp value to regexp or wildcard token's value.
11377
0
    regexp_value = regexp_or_wildcard_token->value;
11378
0
  }
11379
  // Let type be "regexp".
11380
0
  auto type = url_pattern_part_type::REGEXP;
11381
  // If regexp value is parser's segment wildcard regexp:
11382
0
  if (regexp_value == segment_wildcard_regexp) {
11383
    // Set type to "segment-wildcard".
11384
0
    type = url_pattern_part_type::SEGMENT_WILDCARD;
11385
    // Set regexp value to the empty string.
11386
0
    regexp_value.clear();
11387
0
  } else if (regexp_value == ".*") {
11388
    // Otherwise if regexp value is the full wildcard regexp value:
11389
    // Set type to "full-wildcard".
11390
0
    type = url_pattern_part_type::FULL_WILDCARD;
11391
    // Set regexp value to the empty string.
11392
0
    regexp_value.clear();
11393
0
  }
11394
  // Let name be the empty string.
11395
0
  std::string name{};
11396
  // If name token is not null, then set name to name token's value.
11397
0
  if (name_token) {
11398
0
    name = name_token->value;
11399
0
  } else if (regexp_or_wildcard_token != nullptr) {
11400
    // Otherwise if regexp or wildcard token is not null:
11401
    // Set name to parser's next numeric name, serialized.
11402
0
    name = std::to_string(next_numeric_name);
11403
    // Increment parser's next numeric name by 1.
11404
0
    next_numeric_name++;
11405
0
  }
11406
  // If the result of running is a duplicate name given parser and name is
11407
  // true, then throw a TypeError. Tracked in a set so this stays O(1): a
11408
  // linear scan of the part list here is O(n^2) over a pattern string with n
11409
  // named parts (e.g. "/*" repeated), an algorithmic-complexity DoS.
11410
0
  if (!name.empty() && !seen_names.insert(name).second) {
11411
0
    return errors::type_error;
11412
0
  }
11413
  // Let encoded prefix be the result of running parser's encoding callback
11414
  // given prefix.
11415
0
  auto encoded_prefix = encoding_callback(prefix);
11416
0
  if (!encoded_prefix) return encoded_prefix.error();
11417
  // Let encoded suffix be the result of running parser's encoding callback
11418
  // given suffix.
11419
0
  auto encoded_suffix = encoding_callback(suffix);
11420
0
  if (!encoded_suffix) return encoded_suffix.error();
11421
  // Let part be a new part whose type is type, value is regexp value,
11422
  // modifier is modifier, name is name, prefix is encoded prefix, and suffix
11423
  // is encoded suffix.
11424
  // Append part to parser's part list.
11425
0
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
11426
0
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
11427
0
  return std::nullopt;
11428
0
}
11429
11430
template <url_pattern_encoding_callback F>
11431
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
11432
    std::string_view input, url_pattern_compile_component_options& options,
11433
0
    F& encoding_callback) {
11434
0
  ada_log("parse_pattern_string input=", input);
11435
  // Let parser be a new pattern parser whose encoding callback is encoding
11436
  // callback and segment wildcard regexp is the result of running generate a
11437
  // segment wildcard regexp given options.
11438
0
  auto parser = url_pattern_parser<F>(
11439
0
      encoding_callback, generate_segment_wildcard_regexp(options));
11440
  // Set parser's token list to the result of running tokenize given input and
11441
  // "strict".
11442
0
  auto tokenize_result = tokenize(input, token_policy::strict);
11443
0
  if (!tokenize_result) {
11444
0
    ada_log("parse_pattern_string tokenize failed");
11445
0
    return tl::unexpected(tokenize_result.error());
11446
0
  }
11447
0
  parser.tokens = std::move(*tokenize_result);
11448
11449
  // While parser's index is less than parser's token list's size:
11450
0
  while (parser.can_continue()) {
11451
    // Let char token be the result of running try to consume a token given
11452
    // parser and "char".
11453
0
    auto char_token = parser.try_consume_token(token_type::CHAR);
11454
    // Let name token be the result of running try to consume a token given
11455
    // parser and "name".
11456
0
    auto name_token = parser.try_consume_token(token_type::NAME);
11457
    // Let regexp or wildcard token be the result of running try to consume a
11458
    // regexp or wildcard token given parser and name token.
11459
0
    auto regexp_or_wildcard_token =
11460
0
        parser.try_consume_regexp_or_wildcard_token(name_token);
11461
    // If name token is not null or regexp or wildcard token is not null:
11462
0
    if (name_token || regexp_or_wildcard_token) {
11463
      // Let prefix be the empty string.
11464
0
      std::string prefix{};
11465
      // If char token is not null then set prefix to char token's value.
11466
0
      if (char_token) prefix = char_token->value;
11467
      // If prefix is not the empty string and not options's prefix code point:
11468
0
      if (!prefix.empty() && prefix != options.get_prefix()) {
11469
        // Append prefix to the end of parser's pending fixed value.
11470
0
        parser.pending_fixed_value.append(prefix);
11471
        // Set prefix to the empty string.
11472
0
        prefix.clear();
11473
0
      }
11474
      // Run maybe add a part from the pending fixed value given parser.
11475
0
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11476
0
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
11477
0
        return tl::unexpected(*error);
11478
0
      }
11479
      // Let modifier token be the result of running try to consume a modifier
11480
      // token given parser.
11481
0
      auto modifier_token = parser.try_consume_modifier_token();
11482
      // Run add a part given parser, prefix, name token, regexp or wildcard
11483
      // token, the empty string, and modifier token.
11484
0
      if (auto error =
11485
0
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
11486
0
                              modifier_token)) {
11487
0
        ada_log("parser.add_part failed");
11488
0
        return tl::unexpected(*error);
11489
0
      }
11490
      // Continue.
11491
0
      continue;
11492
0
    }
11493
11494
    // Let fixed token be char token.
11495
0
    auto fixed_token = char_token;
11496
    // If fixed token is null, then set fixed token to the result of running try
11497
    // to consume a token given parser and "escaped-char".
11498
0
    if (!fixed_token)
11499
0
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
11500
    // If fixed token is not null:
11501
0
    if (fixed_token) {
11502
      // Append fixed token's value to parser's pending fixed value.
11503
0
      parser.pending_fixed_value.append(fixed_token->value);
11504
      // Continue.
11505
0
      continue;
11506
0
    }
11507
    // Let open token be the result of running try to consume a token given
11508
    // parser and "open".
11509
0
    auto open_token = parser.try_consume_token(token_type::OPEN);
11510
    // If open token is not null:
11511
0
    if (open_token) {
11512
      // Set prefix be the result of running consume text given parser.
11513
0
      auto prefix_ = parser.consume_text();
11514
      // Set name token to the result of running try to consume a token given
11515
      // parser and "name".
11516
0
      name_token = parser.try_consume_token(token_type::NAME);
11517
      // Set regexp or wildcard token to the result of running try to consume a
11518
      // regexp or wildcard token given parser and name token.
11519
0
      regexp_or_wildcard_token =
11520
0
          parser.try_consume_regexp_or_wildcard_token(name_token);
11521
      // Let suffix be the result of running consume text given parser.
11522
0
      auto suffix_ = parser.consume_text();
11523
      // Run consume a required token given parser and "close".
11524
0
      if (!parser.consume_required_token(token_type::CLOSE)) {
11525
0
        ada_log("parser.consume_required_token failed");
11526
0
        return tl::unexpected(errors::type_error);
11527
0
      }
11528
      // Set modifier token to the result of running try to consume a modifier
11529
      // token given parser.
11530
0
      auto modifier_token = parser.try_consume_modifier_token();
11531
      // Run add a part given parser, prefix, name token, regexp or wildcard
11532
      // token, suffix, and modifier token.
11533
0
      if (auto error =
11534
0
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
11535
0
                              suffix_, modifier_token)) {
11536
0
        return tl::unexpected(*error);
11537
0
      }
11538
      // Continue.
11539
0
      continue;
11540
0
    }
11541
    // Run maybe add a part from the pending fixed value given parser.
11542
0
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11543
0
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
11544
0
      return tl::unexpected(*error);
11545
0
    }
11546
    // Run consume a required token given parser and "end".
11547
0
    if (!parser.consume_required_token(token_type::END)) {
11548
0
      return tl::unexpected(errors::type_error);
11549
0
    }
11550
0
  }
11551
0
  ada_log("parser.parts size is: ", parser.parts.size());
11552
  // Return parser's part list.
11553
0
  return parser.parts;
11554
0
}
11555
11556
template <url_pattern_regex::regex_concept regex_provider>
11557
bool protocol_component_matches_special_scheme(
11558
    url_pattern_component<regex_provider>& component) {
11559
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
11560
  switch (component.type) {
11561
    case url_pattern_component_type::EMPTY:
11562
      // Empty pattern can't match any special scheme
11563
      return false;
11564
    case url_pattern_component_type::EXACT_MATCH:
11565
      // Direct string comparison for exact match patterns
11566
      return component.exact_match_value == "http" ||
11567
             component.exact_match_value == "https" ||
11568
             component.exact_match_value == "ws" ||
11569
             component.exact_match_value == "wss" ||
11570
             component.exact_match_value == "ftp" ||
11571
             component.exact_match_value == "file";
11572
    case url_pattern_component_type::FULL_WILDCARD:
11573
      // Full wildcard matches everything including special schemes
11574
      return true;
11575
    case url_pattern_component_type::REGEXP:
11576
      // Fall back to regex matching for complex patterns
11577
      auto& regex = component.regexp;
11578
      return regex_provider::regex_match("http", regex) ||
11579
             regex_provider::regex_match("https", regex) ||
11580
             regex_provider::regex_match("ws", regex) ||
11581
             regex_provider::regex_match("wss", regex) ||
11582
             regex_provider::regex_match("ftp", regex) ||
11583
             regex_provider::regex_match("file", regex);
11584
  }
11585
  ada::unreachable();
11586
}
11587
11588
template <url_pattern_regex::regex_concept regex_provider>
11589
inline std::optional<errors> constructor_string_parser<
11590
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
11591
  ada_log(
11592
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
11593
      "flag");
11594
  // Let protocol string be the result of running make a component string given
11595
  // parser.
11596
  auto protocol_string = make_component_string();
11597
  // Let protocol component be the result of compiling a component given
11598
  // protocol string, canonicalize a protocol, and default options.
11599
  auto protocol_component = url_pattern_component<regex_provider>::compile(
11600
      protocol_string, canonicalize_protocol,
11601
      url_pattern_compile_component_options::DEFAULT);
11602
  if (!protocol_component) {
11603
    ada_log("url_pattern_component::compile failed for protocol_string ",
11604
            protocol_string);
11605
    return protocol_component.error();
11606
  }
11607
  // If the result of running protocol component matches a special scheme given
11608
  // protocol component is true, then set parser's protocol matches a special
11609
  // scheme flag to true.
11610
  if (protocol_component_matches_special_scheme(*protocol_component)) {
11611
    protocol_matches_a_special_scheme_flag = true;
11612
  }
11613
  return std::nullopt;
11614
}
11615
11616
template <url_pattern_regex::regex_concept regex_provider>
11617
tl::expected<url_pattern_init, errors>
11618
constructor_string_parser<regex_provider>::parse(std::string_view input) {
11619
  ada_log("constructor_string_parser::parse input=", input);
11620
  // Let parser be a new constructor string parser whose input is input and
11621
  // token list is the result of running tokenize given input and "lenient".
11622
  auto token_list = tokenize(input, token_policy::lenient);
11623
  if (!token_list) {
11624
    return tl::unexpected(token_list.error());
11625
  }
11626
  auto parser = constructor_string_parser(input, std::move(*token_list));
11627
11628
  // While parser's token index is less than parser's token list size:
11629
  while (parser.token_index < parser.token_list.size()) {
11630
    // Set parser's token increment to 1.
11631
    parser.token_increment = 1;
11632
11633
    // If parser's token list[parser's token index]'s type is "end" then:
11634
    if (parser.token_list[parser.token_index].type == token_type::END) {
11635
      // If parser's state is "init":
11636
      if (parser.state == State::INIT) {
11637
        // Run rewind given parser.
11638
        parser.rewind();
11639
        // If the result of running is a hash prefix given parser is true, then
11640
        // run change state given parser, "hash" and 1.
11641
        if (parser.is_hash_prefix()) {
11642
          parser.change_state(State::HASH, 1);
11643
        } else if (parser.is_search_prefix()) {
11644
          // Otherwise if the result of running is a search prefix given parser
11645
          // is true: Run change state given parser, "search" and 1.
11646
          parser.change_state(State::SEARCH, 1);
11647
        } else {
11648
          // Run change state given parser, "pathname" and 0.
11649
          parser.change_state(State::PATHNAME, 0);
11650
        }
11651
        // Increment parser's token index by parser's token increment.
11652
        parser.token_index += parser.token_increment;
11653
        // Continue.
11654
        continue;
11655
      }
11656
11657
      if (parser.state == State::AUTHORITY) {
11658
        // If parser's state is "authority":
11659
        // Run rewind and set state given parser, and "hostname".
11660
        parser.rewind();
11661
        parser.change_state(State::HOSTNAME, 0);
11662
        // Increment parser's token index by parser's token increment.
11663
        parser.token_index += parser.token_increment;
11664
        // Continue.
11665
        continue;
11666
      }
11667
11668
      // Run change state given parser, "done" and 0.
11669
      parser.change_state(State::DONE, 0);
11670
      // Break.
11671
      break;
11672
    }
11673
11674
    // If the result of running is a group open given parser is true:
11675
    if (parser.is_group_open()) {
11676
      // Increment parser's group depth by 1.
11677
      parser.group_depth += 1;
11678
      // Increment parser's token index by parser's token increment.
11679
      parser.token_index += parser.token_increment;
11680
      // Continue.
11681
      continue;
11682
    }
11683
11684
    // If parser's group depth is greater than 0:
11685
    if (parser.group_depth > 0) {
11686
      // If the result of running is a group close given parser is true, then
11687
      // decrement parser's group depth by 1.
11688
      if (parser.is_group_close()) {
11689
        parser.group_depth -= 1;
11690
      } else {
11691
        // Increment parser's token index by parser's token increment.
11692
        parser.token_index += parser.token_increment;
11693
        continue;
11694
      }
11695
    }
11696
11697
    // Switch on parser's state and run the associated steps:
11698
    switch (parser.state) {
11699
      case State::INIT: {
11700
        // If the result of running is a protocol suffix given parser is true:
11701
        if (parser.is_protocol_suffix()) {
11702
          // Run rewind and set state given parser and "protocol".
11703
          parser.rewind();
11704
          parser.change_state(State::PROTOCOL, 0);
11705
        }
11706
        break;
11707
      }
11708
      case State::PROTOCOL: {
11709
        // If the result of running is a protocol suffix given parser is true:
11710
        if (parser.is_protocol_suffix()) {
11711
          // Run compute protocol matches a special scheme flag given parser.
11712
          if (const auto error =
11713
                  parser.compute_protocol_matches_special_scheme_flag()) {
11714
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11715
            return tl::unexpected(*error);
11716
          }
11717
          // Let next state be "pathname".
11718
          auto next_state = State::PATHNAME;
11719
          // Let skip be 1.
11720
          auto skip = 1;
11721
          // If the result of running next is authority slashes given parser is
11722
          // true:
11723
          if (parser.next_is_authority_slashes()) {
11724
            // Set next state to "authority".
11725
            next_state = State::AUTHORITY;
11726
            // Set skip to 3.
11727
            skip = 3;
11728
          } else if (parser.protocol_matches_a_special_scheme_flag) {
11729
            // Otherwise if parser's protocol matches a special scheme flag is
11730
            // true, then set next state to "authority".
11731
            next_state = State::AUTHORITY;
11732
          }
11733
11734
          // Run change state given parser, next state, and skip.
11735
          parser.change_state(next_state, skip);
11736
        }
11737
        break;
11738
      }
11739
      case State::AUTHORITY: {
11740
        // If the result of running is an identity terminator given parser is
11741
        // true, then run rewind and set state given parser and "username".
11742
        if (parser.is_an_identity_terminator()) {
11743
          parser.rewind();
11744
          parser.change_state(State::USERNAME, 0);
11745
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11746
                   parser.is_hash_prefix()) {
11747
          // Otherwise if any of the following are true:
11748
          // - the result of running is a pathname start given parser;
11749
          // - the result of running is a search prefix given parser; or
11750
          // - the result of running is a hash prefix given parser,
11751
          // then run rewind and set state given parser and "hostname".
11752
          parser.rewind();
11753
          parser.change_state(State::HOSTNAME, 0);
11754
        }
11755
        break;
11756
      }
11757
      case State::USERNAME: {
11758
        // If the result of running is a password prefix given parser is true,
11759
        // then run change state given parser, "password", and 1.
11760
        if (parser.is_password_prefix()) {
11761
          parser.change_state(State::PASSWORD, 1);
11762
        } else if (parser.is_an_identity_terminator()) {
11763
          // Otherwise if the result of running is an identity terminator given
11764
          // parser is true, then run change state given parser, "hostname",
11765
          // and 1.
11766
          parser.change_state(State::HOSTNAME, 1);
11767
        }
11768
        break;
11769
      }
11770
      case State::PASSWORD: {
11771
        // If the result of running is an identity terminator given parser is
11772
        // true, then run change state given parser, "hostname", and 1.
11773
        if (parser.is_an_identity_terminator()) {
11774
          parser.change_state(State::HOSTNAME, 1);
11775
        }
11776
        break;
11777
      }
11778
      case State::HOSTNAME: {
11779
        // If the result of running is an IPv6 open given parser is true, then
11780
        // increment parser's hostname IPv6 bracket depth by 1.
11781
        if (parser.is_an_ipv6_open()) {
11782
          parser.hostname_ipv6_bracket_depth += 1;
11783
        } else if (parser.is_an_ipv6_close()) {
11784
          // Otherwise if the result of running is an IPv6 close given parser is
11785
          // true, then decrement parser's hostname IPv6 bracket depth by 1.
11786
          parser.hostname_ipv6_bracket_depth -= 1;
11787
        } else if (parser.is_port_prefix() &&
11788
                   parser.hostname_ipv6_bracket_depth == 0) {
11789
          // Otherwise if the result of running is a port prefix given parser is
11790
          // true and parser's hostname IPv6 bracket depth is zero, then run
11791
          // change state given parser, "port", and 1.
11792
          parser.change_state(State::PORT, 1);
11793
        } else if (parser.is_pathname_start()) {
11794
          // Otherwise if the result of running is a pathname start given parser
11795
          // is true, then run change state given parser, "pathname", and 0.
11796
          parser.change_state(State::PATHNAME, 0);
11797
        } else if (parser.is_search_prefix()) {
11798
          // Otherwise if the result of running is a search prefix given parser
11799
          // is true, then run change state given parser, "search", and 1.
11800
          parser.change_state(State::SEARCH, 1);
11801
        } else if (parser.is_hash_prefix()) {
11802
          // Otherwise if the result of running is a hash prefix given parser is
11803
          // true, then run change state given parser, "hash", and 1.
11804
          parser.change_state(State::HASH, 1);
11805
        }
11806
11807
        break;
11808
      }
11809
      case State::PORT: {
11810
        // If the result of running is a pathname start given parser is true,
11811
        // then run change state given parser, "pathname", and 0.
11812
        if (parser.is_pathname_start()) {
11813
          parser.change_state(State::PATHNAME, 0);
11814
        } else if (parser.is_search_prefix()) {
11815
          // Otherwise if the result of running is a search prefix given parser
11816
          // is true, then run change state given parser, "search", and 1.
11817
          parser.change_state(State::SEARCH, 1);
11818
        } else if (parser.is_hash_prefix()) {
11819
          // Otherwise if the result of running is a hash prefix given parser is
11820
          // true, then run change state given parser, "hash", and 1.
11821
          parser.change_state(State::HASH, 1);
11822
        }
11823
        break;
11824
      }
11825
      case State::PATHNAME: {
11826
        // If the result of running is a search prefix given parser is true,
11827
        // then run change state given parser, "search", and 1.
11828
        if (parser.is_search_prefix()) {
11829
          parser.change_state(State::SEARCH, 1);
11830
        } else if (parser.is_hash_prefix()) {
11831
          // Otherwise if the result of running is a hash prefix given parser is
11832
          // true, then run change state given parser, "hash", and 1.
11833
          parser.change_state(State::HASH, 1);
11834
        }
11835
        break;
11836
      }
11837
      case State::SEARCH: {
11838
        // If the result of running is a hash prefix given parser is true, then
11839
        // run change state given parser, "hash", and 1.
11840
        if (parser.is_hash_prefix()) {
11841
          parser.change_state(State::HASH, 1);
11842
        }
11843
        break;
11844
      }
11845
      case State::HASH: {
11846
        // Do nothing
11847
        break;
11848
      }
11849
      default: {
11850
        // Assert: This step is never reached.
11851
        unreachable();
11852
      }
11853
    }
11854
11855
    // Increment parser's token index by parser's token increment.
11856
    parser.token_index += parser.token_increment;
11857
  }
11858
11859
  // If parser's result contains "hostname" and not "port", then set parser's
11860
  // result["port"] to the empty string.
11861
  if (parser.result.hostname && !parser.result.port) {
11862
    parser.result.port = "";
11863
  }
11864
11865
  // Return parser's result.
11866
  return parser.result;
11867
}
11868
11869
}  // namespace ada::url_pattern_helpers
11870
#endif  // ADA_INCLUDE_URL_PATTERN
11871
#endif
11872
/* end file include/ada/url_pattern_helpers-inl.h */
11873
/* begin file include/ada/url_pattern_list.h */
11874
/**
11875
 * @file url_pattern_list.h
11876
 * @brief Compiled set-level matching over URLPattern pathname patterns.
11877
 *
11878
 * `ada::url_pattern_list` answers the question "which of my N routes matches
11879
 * this pathname, and what are the parameter values?" in a single walk
11880
 * instead of a loop of N `url_pattern::exec` calls. The whole route set is
11881
 * compiled once, by ada::parse_url_pattern_list, into a segment trie with
11882
 * per-node dispatch (direct compares for small fanouts, a first-byte index
11883
 * at the root, perfect-hash projection for wide nodes). Matching a pathname
11884
 * against the compiled set is allocation-free and does not execute any
11885
 * regular expression for routes written in the common static / ":param" /
11886
 * "*" subset; other routes are matched through the regex provider, exactly
11887
 * as ada::url_pattern does.
11888
 *
11889
 * This implements the route-set ("URLPatternList") use case discussed in
11890
 * https://github.com/whatwg/urlpattern/issues/166 for the pathname component.
11891
 *
11892
 * Correctness does not depend on the fast path: inputs or routes that exceed
11893
 * the fast-path limits (documented on the constants in
11894
 * `ada::url_pattern_list_limits`) are matched by a sequential fallback with
11895
 * identical priority semantics, never rejected and never answered wrongly.
11896
 *
11897
 * @see https://urlpattern.spec.whatwg.org/
11898
 */
11899
#ifndef ADA_URL_PATTERN_LIST_H
11900
#define ADA_URL_PATTERN_LIST_H
11901
11902
11903
#include <array>
11904
#include <cstddef>
11905
#include <cstdint>
11906
#include <optional>
11907
#include <span>
11908
#include <string>
11909
#include <string_view>
11910
#include <vector>
11911
11912
#if ADA_INCLUDE_URL_PATTERN
11913
namespace ada {
11914
11915
/**
11916
 * The limits of the url_pattern_list fast path. None of them changes what a
11917
 * list matches: routes and inputs beyond a limit are matched by a sequential
11918
 * fallback with identical semantics.
11919
 * @namespace ada::url_pattern_list_limits
11920
 */
11921
namespace url_pattern_list_limits {
11922
11923
/**
11924
 * Maximum number of captures (":param" groups plus one trailing "*" group)
11925
 * a route may declare and still be compiled into the fast path. Routes with
11926
 * more groups are matched by the sequential fallback; their match is still
11927
 * correct, but only the first `max_captures_per_route` captures are reported
11928
 * (see url_pattern_list_match_result::captures_truncated).
11929
 */
11930
inline constexpr uint32_t max_captures_per_route = 8;
11931
11932
/**
11933
 * Maximum number of '/'-separated segments an input pathname may have and
11934
 * still be matched by the fast path. Longer inputs fall back to the
11935
 * sequential matcher and are still matched correctly.
11936
 */
11937
inline constexpr uint32_t max_fast_path_segments = 24;
11938
11939
/**
11940
 * Maximum input pathname length (bytes) accepted by the fast path; segment
11941
 * offsets are tracked in 16-bit integers internally. Longer inputs fall back
11942
 * to the sequential matcher and are still matched correctly.
11943
 */
11944
inline constexpr uint32_t max_fast_path_pathname_length = 4096;
11945
11946
/**
11947
 * Maximum number of pattern segments a route may have and still be compiled
11948
 * into the trie fast path. Deeper routes are matched by the sequential
11949
 * fallback with identical semantics.
11950
 */
11951
inline constexpr uint32_t max_trie_pattern_segments = 16;
11952
11953
}  // namespace url_pattern_list_limits
11954
11955
/**
11956
 * The result of url_pattern_list::match. A winning route reports its group
11957
 * values in one of two forms, mirroring url_pattern_list::group_names():
11958
 *
11959
 * - Routes in the static / ":param" / "*" subset report zero-allocation
11960
 *   captures: (offset, length) slices of the pathname passed to match, in
11961
 *   capture order (":param" groups left to right, then the "*" group).
11962
 *   `regexp_route` is false and `regexp_groups` is empty.
11963
 * - Routes matched through the regex provider (patterns with custom regexp
11964
 *   groups or "?" / "+" / "*" modifiers) report the group values the
11965
 *   provider's regex_search returned, exactly as url_pattern::exec would for
11966
 *   the pathname component: `regexp_route` is true, `regexp_groups[i]` is the
11967
 *   value of group_names(route_index)[i] (nullopt for a group that did not
11968
 *   participate), and capture_count is 0.
11969
 *
11970
 * Routes with more than url_pattern_list_limits::max_captures_per_route
11971
 * groups report only the first max_captures_per_route slices and set
11972
 * captures_truncated.
11973
 */
11974
struct url_pattern_list_match_result {
11975
  /** One capture: an (offset, length) slice of the matched pathname. */
11976
  struct capture {
11977
    uint32_t offset = 0;
11978
    uint32_t length = 0;
11979
  };
11980
  /** Index of the winning route in the creation order, or -1 for no match. */
11981
  int32_t route_index = -1;
11982
  /** Number of valid entries in captures. */
11983
  uint32_t capture_count = 0;
11984
  /** True when the route has more groups than could be reported as slices. */
11985
  bool captures_truncated = false;
11986
  /** True when the winning route was matched through the regex provider; its
11987
   * group values are in regexp_groups. */
11988
  bool regexp_route = false;
11989
  std::array<capture, url_pattern_list_limits::max_captures_per_route>
11990
      captures{};
11991
  /** Group values of a regexp route, as returned by the provider. */
11992
  std::vector<std::optional<std::string>> regexp_groups{};
11993
11994
0
  [[nodiscard]] bool has_match() const noexcept { return route_index >= 0; }
11995
};
11996
11997
/**
11998
 * @private
11999
 * The compiled tables url_pattern_list::match walks, and the inline matcher
12000
 * over them (url_pattern_list-inl.h). Nothing here is part of the supported
12001
 * API. The route-set compiler that produces these tables is not part of the
12002
 * public headers at all (src/url_pattern_list_compiler.h).
12003
 * @namespace ada::url_pattern_list_detail
12004
 */
12005
namespace url_pattern_list_detail {
12006
12007
/** @private One trie node. Nodes with a projection dispatch keep their hash
12008
 * payload in a separate hash_record, so the common 0/1/direct nodes stay at
12009
 * 24 bytes. */
12010
struct node_record {
12011
  // First static edge (edges are laid out per node, contiguously).
12012
  int32_t first_child = 0;
12013
  // Child node index, -2 - route (pure-leaf shortcut), or -1.
12014
  int32_t param_child = -1;
12015
  // Route index of a "*" ending at this prefix, or -1.
12016
  int32_t wild_route = -1;
12017
  // Route ending exactly here, or -1.
12018
  int32_t terminal_route = -1;
12019
  uint16_t n_static = 0;
12020
  // 0 none, 1 direct compares, 2 projection, 3 linear scan, 4 first-byte
12021
  // index (root only).
12022
  uint8_t dispatch = 0;
12023
  // 1 if the node still has a param/wildcard alternative to backtrack to.
12024
  uint8_t has_alternative = 0;
12025
  // Index of the node's hash_record (dispatch == 2 only).
12026
  uint32_t hash_index = 0;
12027
};
12028
12029
/** @private Projection-dispatch payload of a hashed node. */
12030
struct hash_record {
12031
  uint64_t multiplier = 0;
12032
  uint32_t slot_base = 0;
12033
  // Witness plan: 3 nibble ids | count << 12 | use_len << 14.
12034
  uint16_t witness_plan = 0;
12035
  // Slot table size == 1 << slot_bits.
12036
  uint8_t slot_bits = 0;
12037
  uint8_t reserved = 0;
12038
};
12039
12040
/** @private One static edge; keys <= 16 bytes verify from the packed
12041
 * windows alone. */
12042
struct edge_record {
12043
  // Key bytes [0, min(8, len)), little-endian, zero-padded.
12044
  uint64_t prefix = 0;
12045
  // Key bytes [max(0, len - 8), len), little-endian, zero-padded.
12046
  uint64_t suffix = 0;
12047
  // Child node index; negative values encode -2 - route pure-leaf shortcuts.
12048
  int32_t node = -1;
12049
  // Full key in the blob (read only when key_length > 16).
12050
  uint32_t key_offset = 0;
12051
  uint16_t key_length = 0;
12052
};
12053
12054
/** @private How one route of the set is matched. */
12055
enum class route_mode : uint8_t {
12056
  // Compiled into the trie fast path.
12057
  trie = 0,
12058
  // Safe static/":param"/"*" syntax beyond a fast-path build limit: matched
12059
  // by the sequential segment matcher.
12060
  sequential = 1,
12061
  // Needs URLPattern regexp semantics: matched through the regex provider.
12062
  regexp = 2,
12063
};
12064
12065
/** @private Everything the matcher knows about one route after a hit. */
12066
struct route_record {
12067
  // Packed per-segment kinds, two bits per segment, most significant first;
12068
  // (kind_sequence, kind_length, route index) compared as a tuple is the
12069
  // specificity order.
12070
  uint64_t kind_sequence = 0;
12071
  // Sequential-mode routes: their segments in the segment table.
12072
  uint32_t segment_first = 0;
12073
  uint32_t segment_count = 0;
12074
  // Auxiliary routes that can outrank this route when it wins the fast
12075
  // path: a range of the aux table (usually empty).
12076
  uint32_t challenger_first = 0;
12077
  uint32_t challenger_count = 0;
12078
  // Ordinal among the regexp-mode routes, or -1.
12079
  int32_t regexp_component = -1;
12080
  uint8_t kind_length = 0;
12081
  route_mode mode = route_mode::sequential;
12082
  // Trie-mode routes: capture positions.
12083
  uint8_t n_params = 0;
12084
  uint8_t wild = 0;
12085
  std::array<uint8_t, url_pattern_list_limits::max_captures_per_route>
12086
      param_positions{};
12087
};
12088
12089
/** @private One pattern segment of a sequential-mode route. */
12090
struct segment_record {
12091
  uint32_t text_offset = 0;
12092
  uint32_t text_length = 0;
12093
  // 0 literal, 1 ":param", 2 "*".
12094
  uint8_t kind = 0;
12095
};
12096
12097
/**
12098
 * @private
12099
 * The compiled route set: one arena holding every table, addressed by
12100
 * section offsets, plus the per-route group names. Sections are 8-byte
12101
 * aligned; the blob is zero-padded by 8 bytes so short key windows can be
12102
 * loaded whole.
12103
 */
12104
struct compiled_routes {
12105
  std::vector<uint8_t> arena{};
12106
  std::vector<std::vector<std::string>> group_names{};
12107
  uint32_t nodes_offset = 0;
12108
  uint32_t hashes_offset = 0;
12109
  uint32_t edges_offset = 0;
12110
  uint32_t slots_offset = 0;
12111
  uint32_t blob_offset = 0;
12112
  uint32_t routes_offset = 0;
12113
  uint32_t segments_offset = 0;
12114
  uint32_t aux_offset = 0;
12115
  uint32_t root_index_offset = 0;
12116
  uint32_t n_routes = 0;
12117
  // All auxiliary routes (regexp mode and sequential mode) in insertion
12118
  // order: the aux table's first n_aux_all entries; challenger ranges
12119
  // follow.
12120
  uint32_t n_aux_all = 0;
12121
  uint8_t ignore_case = 0;
12122
12123
  template <typename T>
12124
0
  [[nodiscard]] const T* section(uint32_t offset) const noexcept {
12125
0
    return reinterpret_cast<const T*>(arena.data() + offset);
12126
0
  }
Unexecuted instantiation: char const* ada::url_pattern_list_detail::compiled_routes::section<char>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::route_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::route_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::segment_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::segment_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::node_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::node_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::hash_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::hash_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::edge_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::edge_record>(unsigned int) const
Unexecuted instantiation: unsigned char const* ada::url_pattern_list_detail::compiled_routes::section<unsigned char>(unsigned int) const
Unexecuted instantiation: unsigned short const* ada::url_pattern_list_detail::compiled_routes::section<unsigned short>(unsigned int) const
12127
};
12128
12129
/** @private Result of the fast-path engine or of the sequential matcher. */
12130
struct engine_result {
12131
  using capture = url_pattern_list_match_result::capture;
12132
  int32_t route = -1;
12133
  uint32_t capture_count = 0;
12134
  // False when the input exceeded a fast-path limit and the caller must run
12135
  // the sequential fallback over the whole route set.
12136
  bool within_fast_path = true;
12137
  bool captures_truncated = false;
12138
  std::array<capture, url_pattern_list_limits::max_captures_per_route>
12139
      captures{};
12140
};
12141
12142
/**
12143
 * @private
12144
 * Parses and compiles a set of pathname patterns (already base-URL
12145
 * processed). Routes that need the regex provider come back with
12146
 * route_record::regexp_component set to their ordinal in insertion order;
12147
 * the caller compiles those components and fills their group names.
12148
 */
12149
tl::expected<compiled_routes, errors> compile_pathname_patterns(
12150
    std::span<const std::string> patterns, bool ignore_case);
12151
12152
/**
12153
 * @private
12154
 * Sequential reference matcher for one non-regexp route of the compiled set;
12155
 * matches any input (no fast-path limits) with the same semantics as the
12156
 * compiled engine. With `fold_input`, input bytes are ASCII case-folded on
12157
 * the fly (the compiled literals are already folded). On a match, fills
12158
 * `result` (route index is NOT set) and returns true.
12159
 */
12160
bool match_route_sequential(const compiled_routes& tables, uint32_t route,
12161
                            std::string_view pathname, bool fold_input,
12162
                            engine_result& result) noexcept;
12163
12164
/**
12165
 * @private
12166
 * True when the bytes [p, p + n) hold no line terminator. A "*" segment
12167
 * compiles to "(.*)" in the URLPattern regexp, and "." in an ECMAScript
12168
 * regular expression does not match LF or CR, so a wildcard may not capture
12169
 * past one. Deliberately not inlined into the walk (it costs the static and
12170
 * ":param" paths, which never run it), and deliberately defined in
12171
 * url_pattern_list.cpp rather than here: MSVC's ada_never_inline carries no
12172
 * inline linkage, so a definition in this header is emitted in every
12173
 * translation unit.
12174
 */
12175
bool wildcard_tail_ok(const char* p, uint32_t n) noexcept;
12176
12177
/**
12178
 * @private
12179
 * Cheap pre-check for a regexp-mode route before running the provider: the
12180
 * part of the route's segment shape that is certain (see the compiler's
12181
 * approximate_kind_sequence) must fit the pathname -- its anchored literal
12182
 * segments, and its exact segment count when every group is a ":name"
12183
 * segment wildcard. False proves the regular expression cannot match.
12184
 */
12185
bool match_regexp_shape(const compiled_routes& tables, uint32_t route,
12186
                        std::string_view pathname, bool fold_input) noexcept;
12187
12188
/**
12189
 * @private
12190
 * True when route `a` (at insertion index `a_index`) outranks route `b` (at
12191
 * `b_index`): lexicographically smaller kind sequence, insertion order as the
12192
 * tiebreak.
12193
 */
12194
constexpr bool outranks(uint64_t a_sequence, uint32_t a_length, size_t a_index,
12195
                        uint64_t b_sequence, uint32_t b_length,
12196
0
                        size_t b_index) noexcept {
12197
0
  if (a_sequence != b_sequence) {
12198
0
    return a_sequence < b_sequence;
12199
0
  }
12200
0
  if (a_length != b_length) {
12201
0
    return a_length < b_length;
12202
0
  }
12203
0
  return a_index < b_index;
12204
0
}
12205
12206
}  // namespace url_pattern_list_detail
12207
12208
template <url_pattern_regex::regex_concept regex_provider>
12209
class url_pattern_list;
12210
12211
/**
12212
 * @private
12213
 * url_pattern_list befriends these two specializations, so they have to be
12214
 * declared before it: a friend declaration that redeclares the function
12215
 * template instead is not matched by every compiler we support (Apple clang
12216
 * 15 rejects the resulting member access). The documented declarations are
12217
 * in implementation.h, next to parse_url_pattern, and carry the default
12218
 * arguments: this header includes url_pattern.h, which includes
12219
 * implementation.h, so those declarations are always seen first, and a
12220
 * function template may not gain default arguments in a later declaration.
12221
 */
12222
template <url_pattern_regex::regex_concept regex_provider>
12223
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12224
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
12225
                       const std::string_view* base_url,
12226
                       const url_pattern_options* options);
12227
12228
/** @private See above. */
12229
template <url_pattern_regex::regex_concept regex_provider>
12230
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12231
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns);
12232
12233
/**
12234
 * @brief A compiled set of URLPattern pathname patterns with one-shot
12235
 * matching.
12236
 *
12237
 * Build once with ada::parse_url_pattern_list, then call match() per
12238
 * request. Matching within the fast-path limits is allocation-free and
12239
 * regex-free for routes written in the static / ":param" / "*" subset; other
12240
 * routes are matched through the regex provider (the same regex_search call
12241
 * url_pattern::exec makes) and still participate in the priority order.
12242
 *
12243
 * Match priority is specificity order, not registration order: routes are
12244
 * compared by their per-segment kind sequence (literal < ":param" < "*",
12245
 * lexicographically from the first segment), with insertion order breaking
12246
 * ties. This is the priority scheme of find-my-way (Fastify) and Express-
12247
 * style routers. Whether a standardized URLPatternList should instead use
12248
 * pure first-match-in-insertion-order semantics is an open question; the
12249
 * compiled representation supports either, and the priority rule is
12250
 * deliberately centralized in url_pattern_list_detail::outranks.
12251
 *
12252
 * Scope (v1): the pathname component only. Other URL components are treated
12253
 * as fully wildcarded; match() takes an already-extracted pathname (for
12254
 * example ada::url_aggregator::get_pathname()). Inputs are matched as given
12255
 * and are expected to be in canonical (percent-encoded) form; the pattern
12256
 * side is canonicalized by the URLPattern pattern parser at creation. The
12257
 * subset follows the URLPattern regexp it stands for: a ":param" segment
12258
 * ("[^/]+?") is one non-empty segment, and a "*" tail ("(.*)") matches any
12259
 * bytes except the line terminators LF and CR, which "." never matches.
12260
 *
12261
 * With url_pattern_options::ignore_case, regexp routes are compiled through
12262
 * the provider with the flag set (as url_pattern does), and the static /
12263
 * ":param" / "*" subset compares literal segments with ASCII case folding,
12264
 * which is what a case-insensitive regular expression does over the ASCII
12265
 * text of a canonical pathname.
12266
 *
12267
 * @tparam regex_provider The regex implementation used only for routes that
12268
 *         need URLPattern regexp semantics. Must satisfy
12269
 *         url_pattern_regex::regex_concept.
12270
 */
12271
template <url_pattern_regex::regex_concept regex_provider>
12272
class url_pattern_list {
12273
 public:
12274
  url_pattern_list() = default;
12275
12276
  /**
12277
   * Matches a pathname against the set and returns the winning route with
12278
   * its captures (see url_pattern_list_match_result). The fast path is
12279
   * allocation-free; inputs beyond the fast-path limits are matched by the
12280
   * sequential fallback with identical semantics.
12281
   */
12282
  [[nodiscard]] url_pattern_list_match_result match(
12283
      std::string_view pathname) const;
12284
12285
  /** Number of routes in the set. */
12286
  [[nodiscard]] size_t size() const noexcept { return patterns_.size(); }
12287
12288
  /** The pathname pattern string the route at `route_index` was created
12289
   * from (after base URL processing, if a base URL was given). */
12290
  [[nodiscard]] std::string_view pattern(size_t route_index) const
12291
      ada_lifetime_bound {
12292
    return patterns_[route_index];
12293
  }
12294
12295
  /**
12296
   * Capture group names of the route at `route_index`, in the order of
12297
   * url_pattern_list_match_result::captures / regexp_groups ("*" groups have
12298
   * numeric names, per URLPattern).
12299
   */
12300
  [[nodiscard]] const std::vector<std::string>& group_names(
12301
      size_t route_index) const ada_lifetime_bound {
12302
    return compiled_.group_names[route_index];
12303
  }
12304
12305
  /** The ignore_case option the set was created with. */
12306
  [[nodiscard]] bool ignore_case() const noexcept {
12307
    return compiled_.ignore_case != 0;
12308
  }
12309
12310
  friend tl::expected<url_pattern_list, errors>
12311
  parse_url_pattern_list<regex_provider>(
12312
      std::span<const std::string_view> pathname_patterns,
12313
      const std::string_view* base_url, const url_pattern_options* options);
12314
12315
  friend tl::expected<url_pattern_list, errors> parse_url_pattern_list<
12316
      regex_provider>(std::span<const url_pattern<regex_provider>> patterns);
12317
12318
 private:
12319
  /** @private Compiles the (already processed) pattern strings; the regexp
12320
   * components are then supplied by the caller in insertion order. */
12321
  static tl::expected<url_pattern_list, errors> create(
12322
      std::vector<std::string>&& pathname_patterns, bool ignore_case);
12323
12324
  /** @private Tests one auxiliary route (sequential or regexp mode) and,
12325
   * when it matches and outranks `best`, replaces `best` with it. `probe`
12326
   * is the (possibly case-folded) pathname for the sequential matcher,
12327
   * `pathname` the original for the provider. */
12328
  void consider_route(
12329
      uint32_t route_index, std::string_view pathname, std::string_view probe,
12330
      bool fold_input, url_pattern_list_detail::engine_result& best,
12331
      std::vector<std::optional<std::string>>& best_groups) const;
12332
12333
  /** @private The pattern strings, by route index. */
12334
  std::vector<std::string> patterns_{};
12335
  /** @private The compiled tables. */
12336
  url_pattern_list_detail::compiled_routes compiled_{};
12337
  /** @private Compiled pathname components of the regexp-mode routes, in
12338
   * insertion order. */
12339
  std::vector<url_pattern_component<regex_provider>> regexp_components_{};
12340
};
12341
12342
}  // namespace ada
12343
#endif  // ADA_INCLUDE_URL_PATTERN
12344
#endif  // ADA_URL_PATTERN_LIST_H
12345
/* end file include/ada/url_pattern_list.h */
12346
/* begin file include/ada/url_pattern_list-inl.h */
12347
/**
12348
 * @file url_pattern_list-inl.h
12349
 * @brief The url_pattern_list matcher (inline) and the class template's
12350
 * member definitions.
12351
 *
12352
 * The walk over the compiled tables lives here, as ada_really_inline
12353
 * functions, so that it inlines into url_pattern_list::match; the route-set
12354
 * compiler that builds the tables is in its own translation unit
12355
 * (src/url_pattern_list.cpp) and is not part of the public headers.
12356
 */
12357
#ifndef ADA_URL_PATTERN_LIST_INL_H
12358
#define ADA_URL_PATTERN_LIST_INL_H
12359
12360
12361
#include <bit>
12362
#include <cstdint>
12363
#include <cstring>
12364
#include <optional>
12365
#include <string>
12366
#include <string_view>
12367
#include <utility>
12368
#include <vector>
12369
12370
#if ADA_NEON
12371
#include <arm_neon.h>
12372
#endif
12373
12374
#if ADA_INCLUDE_URL_PATTERN
12375
namespace ada::url_pattern_list_detail {
12376
12377
// ---- byte helpers ----------------------------------------------------------
12378
12379
// Unaligned 8-byte little-endian load; all 8 bytes must be readable. The
12380
// byte-assembly fallback keeps big-endian targets correct (the compiler
12381
// lowers it to a single load plus byte swap).
12382
0
ada_really_inline uint64_t load8_le(const char* p) noexcept {
12383
0
  if constexpr (std::endian::native == std::endian::little) {
12384
0
    uint64_t x;
12385
0
    std::memcpy(&x, p, 8);
12386
0
    return x;
12387
  } else {
12388
    uint64_t x = 0;
12389
    for (uint32_t j = 0; j < 8; j++) {
12390
      x |= static_cast<uint64_t>(static_cast<uint8_t>(p[j])) << (8 * j);
12391
    }
12392
    return x;
12393
  }
12394
0
}
12395
12396
// Bytes [0, n) of p packed little-endian, zero-padded; reads exactly n
12397
// bytes (n < 8). This is the byte-compare path for short tails.
12398
0
ada_really_inline uint64_t gather_le(const char* p, uint32_t n) noexcept {
12399
0
  uint64_t x = 0;
12400
0
  for (uint32_t j = 0; j < n; j++) {
12401
0
    x |= static_cast<uint64_t>(static_cast<uint8_t>(p[j])) << (8 * j);
12402
0
  }
12403
0
  return x;
12404
0
}
12405
12406
// Mask selecting the low n bytes, 1 <= n <= 7.
12407
0
constexpr uint64_t low_bytes_mask(uint32_t n) noexcept {
12408
0
  return ~0ull >> (64 - 8 * n);
12409
0
}
12410
12411
// ASCII case folding of n bytes from src into dst (the compiler vectorizes
12412
// this loop). Only ASCII letters change: canonical pathnames are ASCII, and
12413
// this is what a case-insensitive regular expression does over them.
12414
0
inline void ascii_fold(const char* src, uint32_t n, char* dst) noexcept {
12415
0
  for (uint32_t i = 0; i < n; i++) {
12416
0
    const uint8_t c = static_cast<uint8_t>(src[i]);
12417
0
    dst[i] = static_cast<char>(c | ((c >= 'A' && c <= 'Z') ? 0x20u : 0u));
12418
0
  }
12419
0
}
12420
12421
// ---- witness machinery -----------------------------------------------------
12422
// Projection dispatch for wide trie nodes. A witness id names a feature of a
12423
// byte string:
12424
//   0..7   the byte at absolute offset id           (0 when out of range)
12425
//   8..15  the byte at offset id-8 counted from the end
12426
// A projection is three witness bytes plus the length packed into one
12427
// uint64; a perfect multiplier turns it into a slot index. Builder and
12428
// matcher call the same accessors and the same packing, so a slot table
12429
// cannot disagree with the gather that reads it.
12430
12431
0
constexpr uint8_t length_byte(uint32_t len) noexcept {
12432
0
  return len < 255u ? static_cast<uint8_t>(len) : static_cast<uint8_t>(255);
12433
0
}
12434
12435
constexpr uint8_t witness_byte(const char* p, uint32_t len,
12436
0
                               uint8_t id) noexcept {  // ids 0..15
12437
0
  uint32_t o = id & 7u;
12438
0
  bool in = o < len;
12439
0
  uint32_t pos = (id & 8u) ? len - 1u - o : o;  // wraps when !in; masked then
12440
0
  pos = in ? pos : 0u;
12441
0
  uint8_t v = len ? static_cast<uint8_t>(p[pos]) : static_cast<uint8_t>(0);
12442
0
  return in ? v : static_cast<uint8_t>(0);
12443
0
}
12444
12445
constexpr uint64_t pack_projection(uint8_t f0, uint8_t f1, uint8_t f2,
12446
0
                                   uint8_t f3) noexcept {
12447
0
  return (static_cast<uint64_t>(f0) << 24) | (static_cast<uint64_t>(f1) << 16) |
12448
0
         (static_cast<uint64_t>(f2) << 8) | static_cast<uint64_t>(f3);
12449
0
}
12450
12451
// Perfect-multiplier slot index; b <= 12 everywhere, so the shift is in
12452
// range and the product needs no modulus.
12453
constexpr uint64_t slot_of(uint64_t proj, uint64_t multiplier,
12454
0
                           uint8_t b) noexcept {
12455
0
  return (proj * multiplier) >> (64 - b);
12456
0
}
12457
12458
// Fixed-shape projection of one string: always 3 witness bytes plus the
12459
// length. Duplicated ids add no information but keep the gather uniform.
12460
constexpr uint64_t project(const char* p, uint32_t len,
12461
0
                           uint16_t plan) noexcept {
12462
0
  return pack_projection(
12463
0
      witness_byte(p, len, static_cast<uint8_t>(plan & 15u)),
12464
0
      witness_byte(p, len, static_cast<uint8_t>((plan >> 4) & 15u)),
12465
0
      witness_byte(p, len, static_cast<uint8_t>((plan >> 8) & 15u)),
12466
0
      length_byte(len));
12467
0
}
12468
12469
// ---- segment scan ----------------------------------------------------------
12470
12471
// Splits the pathname into segments in one pass, in place: only segment
12472
// starts are recorded, with a sentinel soff[nseg] = ulen + 1 so that
12473
// seg_len(i) == soff[i + 1] - soff[i] - 1. Returns the segment count, or 0
12474
// when the input has more than max_fast_path_segments segments (the caller
12475
// falls back to the sequential matcher). Requires ulen >= 1 and
12476
// url[0] == '/'.
12477
//
12478
// Two implementations with the same contract: the portable SWAR scan (8
12479
// bytes per step, an exact zero-byte test on x ^ '/'-fill with no false
12480
// positives, bytes >= 0x80 included, and a byte-exact partial load for the
12481
// tail, so the input is never over-read), and on AArch64 a NEON scan for
12482
// inputs of 16 bytes or more (16 bytes per step, the '/' compare narrowed
12483
// to one nibble per byte, the last block overlapping the previous one so
12484
// the input is never over-read). The 1k+ pathname benchmark in the PR
12485
// discussion is what keeps the NEON scan: it is faster at every length and
12486
// 3-4x faster past 1 KB.
12487
struct segment_emitter {
12488
  uint16_t* soff;
12489
  uint32_t nseg = 0;
12490
  uint32_t start = 1;
12491
  // One segment start per set bit of `lanes`; a lane is 1 << shift bits
12492
  // wide (8 for the SWAR masks, 4 for the NEON nibble masks).
12493
  ada_really_inline bool emit(uint64_t lanes, uint32_t base,
12494
0
                              unsigned shift) noexcept {
12495
0
    using url_pattern_list_limits::max_fast_path_segments;
12496
0
    while (lanes) {
12497
0
      const uint32_t pos =
12498
0
          base + (static_cast<uint32_t>(std::countr_zero(lanes)) >> shift);
12499
0
      if (nseg >= max_fast_path_segments) {
12500
0
        return false;
12501
0
      }
12502
0
      soff[nseg++] = static_cast<uint16_t>(start);
12503
0
      start = pos + 1;
12504
0
      lanes &= lanes - 1;
12505
0
    }
12506
0
    return true;
12507
0
  }
12508
0
  ada_really_inline uint32_t finish(uint32_t ulen) noexcept {
12509
0
    using url_pattern_list_limits::max_fast_path_segments;
12510
0
    if (nseg >= max_fast_path_segments) {
12511
0
      return 0;
12512
0
    }
12513
0
    soff[nseg++] = static_cast<uint16_t>(start);
12514
0
    soff[nseg] = static_cast<uint16_t>(ulen + 1);  // sentinel
12515
0
    return nseg;
12516
0
  }
12517
};
12518
12519
ada_really_inline uint32_t scan_segments_swar(const char* url, uint32_t ulen,
12520
0
                                              uint16_t* soff) noexcept {
12521
0
  constexpr uint64_t slashes = 0x2F2F2F2F2F2F2F2Full;
12522
0
  constexpr uint64_t low7 = 0x7F7F7F7F7F7F7F7Full;
12523
0
  // Bit 7 of every byte of x that is zero, exactly: (b & 0x7F) + 0x7F has
12524
0
  // bit 7 set iff the low bits are non-zero, b itself has it set iff b is
12525
0
  // >= 0x80, and no lane can carry into its neighbour.
12526
0
  const auto zero_lanes = [](uint64_t x) noexcept {
12527
0
    return ~(((x & low7) + low7) | x | low7);
12528
0
  };
12529
0
  segment_emitter out{soff};
12530
0
  uint32_t i = 1;
12531
0
  for (; i + 8 <= ulen; i += 8) {
12532
0
    const uint64_t lanes = zero_lanes(load8_le(url + i) ^ slashes);
12533
0
    if (lanes != 0 && !out.emit(lanes, i, 3)) {
12534
0
      return 0;
12535
0
    }
12536
0
  }
12537
0
  if (i < ulen) {  // tail of 1..7 bytes: zero padding is never a '/'
12538
0
    const uint64_t lanes = zero_lanes(gather_le(url + i, ulen - i) ^ slashes);
12539
0
    if (lanes != 0 && !out.emit(lanes, i, 3)) {
12540
0
      return 0;
12541
0
    }
12542
0
  }
12543
0
  return out.finish(ulen);
12544
0
}
12545
12546
#if ADA_NEON
12547
ada_really_inline uint32_t scan_segments_neon(const char* url, uint32_t ulen,
12548
                                              uint16_t* soff) noexcept {
12549
  // Requires ulen >= 16.
12550
  const uint8x16_t slash = vdupq_n_u8('/');
12551
  // One bit per byte lane that holds a '/': compare, narrow each 16-bit
12552
  // pair to its high nibble, keep bit 0 of every nibble.
12553
  const auto slash_lanes = [&](const char* p) noexcept {
12554
    const uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
12555
    const uint64_t nibbles =
12556
        vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(
12557
                          vreinterpretq_u16_u8(vceqq_u8(v, slash)), 4)),
12558
                      0);
12559
    return nibbles & 0x1111111111111111ull;
12560
  };
12561
  segment_emitter out{soff};
12562
  uint32_t i = 0;
12563
  for (; i + 16 <= ulen; i += 16) {
12564
    uint64_t lanes = slash_lanes(url + i);
12565
    if (i == 0) {
12566
      lanes &= ~0xFull;  // the leading '/' is not a separator
12567
    }
12568
    if (lanes != 0 && !out.emit(lanes, i, 2)) {
12569
      return 0;
12570
    }
12571
  }
12572
  if (i < ulen) {  // overlapped last block: drop the lanes already scanned
12573
    const uint32_t off = ulen - 16;
12574
    const uint64_t lanes = slash_lanes(url + off) & (~0ull << ((i - off) * 4));
12575
    if (lanes != 0 && !out.emit(lanes, off, 2)) {
12576
      return 0;
12577
    }
12578
  }
12579
  return out.finish(ulen);
12580
}
12581
#endif  // ADA_NEON
12582
12583
ada_really_inline uint32_t scan_segments(const char* url, uint32_t ulen,
12584
0
                                         uint16_t* soff) noexcept {
12585
0
#if ADA_NEON
12586
0
  if (ulen >= 16) {
12587
0
    return scan_segments_neon(url, ulen, soff);
12588
0
  }
12589
0
#endif
12590
0
  return scan_segments_swar(url, ulen, soff);
12591
0
}
12592
12593
// ---- verify / dispatch -----------------------------------------------------
12594
12595
// One input segment prepared for dispatch: its bytes, its length, and how
12596
// many bytes are readable from p (to the end of the pathname).
12597
struct segment_ref {
12598
  const char* p;
12599
  uint32_t len;
12600
  uint32_t avail;
12601
};
12602
12603
// Segment-vs-edge compare. Keys of 8..16 bytes verify from two whole-word
12604
// loads inside the segment; shorter keys from one masked load when the
12605
// input has 8 readable bytes, else from a byte gather (short tails such as
12606
// "42" or "me"); longer keys through memcmp against the blob. An empty key
12607
// (a pattern such as "/users/") matches exactly the empty segment.
12608
ada_really_inline bool verify_edge(const edge_record& e, const segment_ref& s,
12609
0
                                   const char* blob) noexcept {
12610
0
  const uint32_t len = s.len;
12611
0
  if (len != e.key_length) {
12612
0
    return false;
12613
0
  }
12614
0
  if (len == 0) {
12615
0
    return true;
12616
0
  }
12617
0
  if (len > 16) {
12618
0
    return std::memcmp(s.p, blob + e.key_offset, len) == 0;
12619
0
  }
12620
0
  if (len >= 8) {
12621
0
    return ((load8_le(s.p) ^ e.prefix) |
12622
0
            (load8_le(s.p + len - 8) ^ e.suffix)) == 0;
12623
0
  }
12624
0
  const uint64_t x = s.avail >= 8 ? (load8_le(s.p) & low_bytes_mask(len))
12625
0
                                  : gather_le(s.p, len);
12626
0
  return x == e.prefix;
12627
0
}
12628
12629
// The typed view of the arena the walk reads; derived once per match.
12630
struct table_view {
12631
  const node_record* nodes;
12632
  const hash_record* hashes;
12633
  const edge_record* edges;
12634
  const uint8_t* slots;
12635
  const char* blob;
12636
  const uint16_t* root_index;
12637
};
12638
12639
0
ada_really_inline table_view view_of(const compiled_routes& r) noexcept {
12640
0
  return table_view{r.section<node_record>(r.nodes_offset),
12641
0
                    r.section<hash_record>(r.hashes_offset),
12642
0
                    r.section<edge_record>(r.edges_offset),
12643
0
                    r.section<uint8_t>(r.slots_offset),
12644
0
                    r.section<char>(r.blob_offset),
12645
0
                    r.section<uint16_t>(r.root_index_offset)};
12646
0
}
12647
12648
// Static-child dispatch: returns the child ordinal within the node or -1.
12649
// Correctness never depends on the projection or the index: the candidate
12650
// is always confirmed by a full segment compare, so a bad table can only
12651
// cost time, never change an answer.
12652
ada_really_inline int32_t dispatch_static(const table_view& t,
12653
                                          const node_record& nd,
12654
0
                                          const segment_ref& s) noexcept {
12655
0
  const edge_record* e = t.edges + nd.first_child;
12656
0
  switch (nd.dispatch) {
12657
0
    case 0:
12658
0
      return -1;
12659
0
    case 1: {  // direct: up to 8 compares, each gated by the key length
12660
0
      for (uint32_t j = 0; j < nd.n_static; j++) {
12661
0
        if (verify_edge(e[j], s, t.blob)) {
12662
0
          return static_cast<int32_t>(j);
12663
0
        }
12664
0
      }
12665
0
      return -1;
12666
0
    }
12667
0
    case 2: {  // projection: gather -> multiply -> slot -> verify
12668
0
      const hash_record& h = t.hashes[nd.hash_index];
12669
0
      const uint64_t proj = project(s.p, s.len, h.witness_plan);
12670
0
      const uint8_t ord =
12671
0
          t.slots[h.slot_base + slot_of(proj, h.multiplier, h.slot_bits)];
12672
0
      if (ord == 0xFF) {
12673
0
        return -1;
12674
0
      }
12675
0
      return verify_edge(e[ord], s, t.blob) ? static_cast<int32_t>(ord) : -1;
12676
0
    }
12677
0
    case 4: {  // root first-byte index: jump to the run of children that
12678
0
               // share the segment's first byte (children are sorted by it)
12679
0
      if (s.len == 0) {
12680
0
        return -1;  // no first byte; an indexed root has no empty key
12681
0
      }
12682
0
      const uint8_t b = static_cast<uint8_t>(s.p[0]);
12683
0
      uint32_t j = t.root_index[b];
12684
0
      if (j == 0xFFFF) {
12685
0
        return -1;
12686
0
      }
12687
0
      for (; j < nd.n_static && static_cast<uint8_t>(e[j].prefix & 0xFFu) == b;
12688
0
           j++) {
12689
0
        if (verify_edge(e[j], s, t.blob)) {
12690
0
          return static_cast<int32_t>(j);
12691
0
        }
12692
0
      }
12693
0
      return -1;
12694
0
    }
12695
0
    default: {  // linear demotion rung (also carries fanout > 254)
12696
0
      for (uint32_t j = 0; j < nd.n_static; j++) {
12697
0
        if (verify_edge(e[j], s, t.blob)) {
12698
0
          return static_cast<int32_t>(j);
12699
0
        }
12700
0
      }
12701
0
      return -1;
12702
0
    }
12703
0
  }
12704
0
}
12705
12706
// ---- the match residual ----------------------------------------------------
12707
12708
// Matches `pathname` against the compiled tables. When the input is out of
12709
// the fast-path contract, returns with within_fast_path == false and no
12710
// answer; the caller falls back to the sequential matcher.
12711
ada_really_inline void match_compiled(const compiled_routes& r, const char* url,
12712
                                      uint32_t ulen,
12713
0
                                      engine_result& out) noexcept {
12714
0
  using url_pattern_list_limits::max_fast_path_pathname_length;
12715
0
  using url_pattern_list_limits::max_fast_path_segments;
12716
0
  out = engine_result{};
12717
0
  if (ulen == 0 || url[0] != '/' || ulen > max_fast_path_pathname_length) {
12718
0
    out.within_fast_path = false;  // the sequential fallback decides
12719
0
    return;
12720
0
  }
12721
0
  const table_view t = view_of(r);
12722
0
  const route_record* routes = r.section<route_record>(r.routes_offset);
12723
0
12724
0
  // Segment scan; overflow means the input is beyond the fast path.
12725
0
  uint16_t soff[max_fast_path_segments + 1];
12726
0
  const uint32_t nseg = scan_segments(url, ulen, soff);
12727
0
  if (nseg == 0) {
12728
0
    out.within_fast_path = false;
12729
0
    return;
12730
0
  }
12731
0
  const auto seg_len = [&](uint32_t i) {
12732
0
    return static_cast<uint32_t>(soff[i + 1]) - soff[i] - 1u;
12733
0
  };
12734
0
  const auto seg_ref = [&](uint32_t i) {
12735
0
    const uint32_t off = soff[i];
12736
0
    return segment_ref{url + off, seg_len(i), ulen - off};
12737
0
  };
12738
0
12739
0
  // Trie walk with an explicit bounded backtrack stack. Every node offers
12740
0
  // the same three alternatives in the compiled priority order (static
12741
0
  // child, param child, wildcard); a backtrack record stores the resume
12742
0
  // point, and a record is pushed only when the node still has an untried
12743
0
  // alternative, so the stack never exceeds the walk depth. Edges encoded
12744
0
  // as -2 - route are pure-leaf shortcuts.
12745
0
  enum alternative : uint16_t {
12746
0
    alt_static = 0,
12747
0
    alt_param = 1,
12748
0
    alt_wild = 2,
12749
0
    alt_none = 3
12750
0
  };
12751
0
  struct backtrack {
12752
0
    int32_t node;
12753
0
    uint16_t i;
12754
0
    uint16_t resume;
12755
0
  };
12756
0
  backtrack stack[max_fast_path_segments + 1];
12757
0
  uint32_t sp = 0;
12758
0
  int32_t node = 0;
12759
0
  uint32_t i = 0;
12760
0
  uint32_t alt = alt_static;
12761
0
  int32_t route = -1;
12762
0
  uint32_t wild_from = 0;
12763
0
  bool wild = false;
12764
0
  for (;;) {
12765
0
    const node_record& nd = t.nodes[static_cast<size_t>(node)];
12766
0
    if (alt == alt_static) {
12767
0
      if (i == nseg) {
12768
0
        if (nd.terminal_route >= 0) {
12769
0
          route = nd.terminal_route;
12770
0
          break;
12771
0
        }
12772
0
        alt = alt_none;
12773
0
      } else {
12774
0
        const int32_t ord = dispatch_static(t, nd, seg_ref(i));
12775
0
        if (ord >= 0) {
12776
0
          const int32_t nxt = t.edges[static_cast<size_t>(nd.first_child) +
12777
0
                                      static_cast<size_t>(ord)]
12778
0
                                  .node;
12779
0
          if (nxt >= 0) {
12780
0
            if (nd.has_alternative) {
12781
0
              stack[sp++] = {node, static_cast<uint16_t>(i), alt_param};
12782
0
            }
12783
0
            node = nxt;
12784
0
            i++;
12785
0
            continue;
12786
0
          }
12787
0
          // Pure-leaf shortcut.
12788
0
          if (i + 1 == nseg) {
12789
0
            route = -2 - nxt;
12790
0
            break;
12791
0
          }
12792
0
          alt = alt_param;  // dead end: fall through to this node's fallbacks
12793
0
        } else {
12794
0
          alt = alt_param;
12795
0
        }
12796
0
      }
12797
0
    }
12798
0
    if (alt == alt_param) {
12799
0
      const int32_t pc = nd.param_child;
12800
0
      if (pc != -1 && seg_len(i) > 0) {
12801
0
        if (pc >= 0) {
12802
0
          if (nd.wild_route >= 0) {
12803
0
            stack[sp++] = {node, static_cast<uint16_t>(i), alt_wild};
12804
0
          }
12805
0
          node = pc;
12806
0
          i++;
12807
0
          alt = alt_static;
12808
0
          continue;
12809
0
        }
12810
0
        // Pure-leaf param shortcut.
12811
0
        if (i + 1 == nseg) {
12812
0
          route = -2 - pc;
12813
0
          break;
12814
0
        }
12815
0
        alt = alt_wild;  // only the wildcard remains here
12816
0
      } else {
12817
0
        alt = alt_wild;
12818
0
      }
12819
0
    }
12820
0
    if (alt == alt_wild) {
12821
0
      if (nd.wild_route >= 0 &&
12822
0
          wildcard_tail_ok(url + soff[i], ulen - soff[i])) {
12823
0
        route = nd.wild_route;
12824
0
        wild_from = i;
12825
0
        wild = true;
12826
0
        break;
12827
0
      }
12828
0
      // No alternative left at this node: resume from the backtrack stack
12829
0
      // (alt is overwritten by the popped record).
12830
0
    }
12831
0
    if (sp == 0) {
12832
0
      break;  // nothing to resume: miss
12833
0
    }
12834
0
    --sp;
12835
0
    node = stack[sp].node;
12836
0
    i = stack[sp].i;
12837
0
    alt = stack[sp].resume;
12838
0
  }
12839
0
12840
0
  out.route = route;
12841
0
  if (route >= 0) {  // capture extraction: params first, wildcard tail last
12842
0
    const route_record& rm = routes[static_cast<size_t>(route)];
12843
0
    uint32_t nc = 0;
12844
0
    for (uint32_t k = 0; k < rm.n_params; k++) {
12845
0
      const uint32_t p = rm.param_positions[k];
12846
0
      out.captures[nc++] = {soff[p], seg_len(p)};
12847
0
    }
12848
0
    if (wild) {
12849
0
      out.captures[nc++] = {soff[wild_from], ulen - soff[wild_from]};
12850
0
    }
12851
0
    out.capture_count = nc;
12852
0
  }
12853
0
}
12854
12855
}  // namespace ada::url_pattern_list_detail
12856
12857
namespace ada {
12858
12859
template <url_pattern_regex::regex_concept regex_provider>
12860
tl::expected<url_pattern_list<regex_provider>, errors>
12861
url_pattern_list<regex_provider>::create(
12862
    std::vector<std::string>&& pathname_patterns, bool ignore_case) {
12863
  url_pattern_list list{};
12864
  list.patterns_ = std::move(pathname_patterns);
12865
  auto compiled = url_pattern_list_detail::compile_pathname_patterns(
12866
      list.patterns_, ignore_case);
12867
  if (!compiled) {
12868
    return tl::unexpected(compiled.error());
12869
  }
12870
  list.compiled_ = std::move(*compiled);
12871
  return list;
12872
}
12873
12874
template <url_pattern_regex::regex_concept regex_provider>
12875
void url_pattern_list<regex_provider>::consider_route(
12876
    uint32_t route_index, std::string_view pathname, std::string_view probe,
12877
    bool fold_input, url_pattern_list_detail::engine_result& best,
12878
    std::vector<std::optional<std::string>>& best_groups) const {
12879
  namespace detail = url_pattern_list_detail;
12880
  const detail::route_record* routes =
12881
      compiled_.section<detail::route_record>(compiled_.routes_offset);
12882
  const detail::route_record& candidate = routes[route_index];
12883
  // Only test candidates that would outrank the current best.
12884
  if (best.route >= 0) {
12885
    const detail::route_record& current =
12886
        routes[static_cast<size_t>(best.route)];
12887
    if (!detail::outranks(candidate.kind_sequence, candidate.kind_length,
12888
                          route_index, current.kind_sequence,
12889
                          current.kind_length,
12890
                          static_cast<size_t>(best.route))) {
12891
      return;
12892
    }
12893
  }
12894
  if (candidate.mode == detail::route_mode::regexp) {
12895
    // A route whose certain segment shape does not fit the input cannot
12896
    // match at all: skip the provider.
12897
    if (!detail::match_regexp_shape(compiled_, route_index, probe,
12898
                                    fold_input)) {
12899
      return;
12900
    }
12901
    // URLPattern's own test/exec split: regex_match answers yes/no (this is
12902
    // what rejects a non-matching route cheaply; a regex_search miss can
12903
    // cost a scan over every start position), and regex_search then
12904
    // produces the group values exactly as url_pattern::exec does.
12905
    const url_pattern_component<regex_provider>& component =
12906
        regexp_components_[static_cast<size_t>(candidate.regexp_component)];
12907
    if (!component.fast_test(pathname)) {
12908
      return;
12909
    }
12910
    auto groups = component.fast_match(pathname);
12911
    if (groups) {
12912
      best = detail::engine_result{};
12913
      best.route = static_cast<int32_t>(route_index);
12914
      best_groups = std::move(*groups);
12915
    }
12916
  } else {
12917
    detail::engine_result scratch{};
12918
    if (detail::match_route_sequential(compiled_, route_index, probe,
12919
                                       fold_input, scratch)) {
12920
      scratch.route = static_cast<int32_t>(route_index);
12921
      best = scratch;
12922
      best_groups.clear();
12923
    }
12924
  }
12925
}
12926
12927
template <url_pattern_regex::regex_concept regex_provider>
12928
url_pattern_list_match_result url_pattern_list<regex_provider>::match(
12929
    std::string_view pathname) const {
12930
  namespace detail = url_pattern_list_detail;
12931
  using url_pattern_list_limits::max_fast_path_pathname_length;
12932
  // A default-constructed list (or one compiled from no routes) has no
12933
  // tables. Walking them would dereference section pointers into an empty
12934
  // arena.
12935
  if (patterns_.empty() || compiled_.arena.empty()) {
12936
    return {};
12937
  }
12938
  // With ignore_case the compiled literals are ASCII-folded, so the fast
12939
  // path probes a folded copy of the input (offsets are unchanged, so the
12940
  // captures still slice the original). Inputs beyond the fast-path length
12941
  // are folded on the fly by the sequential matcher instead.
12942
  char folded[max_fast_path_pathname_length];
12943
  std::string_view probe = pathname;
12944
  bool fold_input = false;
12945
  if (compiled_.ignore_case) {
12946
    if (pathname.size() <= max_fast_path_pathname_length) {
12947
      detail::ascii_fold(pathname.data(),
12948
                         static_cast<uint32_t>(pathname.size()), folded);
12949
      probe = std::string_view(folded, pathname.size());
12950
    } else {
12951
      fold_input = true;
12952
    }
12953
  }
12954
  detail::engine_result best{};
12955
  std::vector<std::optional<std::string>> best_groups{};
12956
  detail::match_compiled(compiled_, probe.data(),
12957
                         static_cast<uint32_t>(probe.size()), best);
12958
  const uint32_t* aux = compiled_.section<uint32_t>(compiled_.aux_offset);
12959
  if (!best.within_fast_path) {
12960
    // The input exceeds a fast-path limit (length, segment count, or no
12961
    // leading '/'): the sequential fallback matches every route with
12962
    // identical priority semantics.
12963
    best = detail::engine_result{};
12964
    for (size_t i = 0; i < patterns_.size(); i++) {
12965
      consider_route(static_cast<uint32_t>(i), pathname, probe, fold_input,
12966
                     best, best_groups);
12967
    }
12968
  } else if (best.route >= 0) {
12969
    // A fast-path winner is final except for the auxiliary routes recorded
12970
    // at creation as able to outrank it (usually none).
12971
    const detail::route_record& winner =
12972
        compiled_.section<detail::route_record>(
12973
            compiled_.routes_offset)[static_cast<size_t>(best.route)];
12974
    for (uint32_t k = 0; k < winner.challenger_count; k++) {
12975
      consider_route(aux[winner.challenger_first + k], pathname, probe,
12976
                     fold_input, best, best_groups);
12977
    }
12978
  } else {
12979
    // A fast-path miss: any auxiliary route may still match.
12980
    for (uint32_t k = 0; k < compiled_.n_aux_all; k++) {
12981
      consider_route(aux[k], pathname, probe, fold_input, best, best_groups);
12982
    }
12983
  }
12984
  url_pattern_list_match_result result{};
12985
  result.route_index = best.route;
12986
  if (best.route >= 0) {
12987
    const detail::route_record& winner =
12988
        compiled_.section<detail::route_record>(
12989
            compiled_.routes_offset)[static_cast<size_t>(best.route)];
12990
    if (winner.mode == detail::route_mode::regexp) {
12991
      result.regexp_route = true;
12992
      result.regexp_groups = std::move(best_groups);
12993
    } else {
12994
      result.capture_count = best.capture_count;
12995
      result.captures_truncated = best.captures_truncated;
12996
      result.captures = best.captures;
12997
    }
12998
  }
12999
  return result;
13000
}
13001
13002
}  // namespace ada
13003
#endif  // ADA_INCLUDE_URL_PATTERN
13004
#endif  // ADA_URL_PATTERN_LIST_INL_H
13005
/* end file include/ada/url_pattern_list-inl.h */
13006
13007
// Public API
13008
/* begin file include/ada/ada_version.h */
13009
/**
13010
 * @file ada_version.h
13011
 * @brief Definitions for Ada's version number.
13012
 */
13013
#ifndef ADA_ADA_VERSION_H
13014
#define ADA_ADA_VERSION_H
13015
13016
0
#define ADA_VERSION "4.0.0"
13017
13018
namespace ada {
13019
13020
enum {
13021
  ADA_VERSION_MAJOR = 4,
13022
  ADA_VERSION_MINOR = 0,
13023
  ADA_VERSION_REVISION = 0,
13024
};
13025
13026
}  // namespace ada
13027
13028
#endif  // ADA_ADA_VERSION_H
13029
/* end file include/ada/ada_version.h */
13030
/* begin file include/ada/implementation-inl.h */
13031
/**
13032
 * @file implementation-inl.h
13033
 */
13034
#ifndef ADA_IMPLEMENTATION_INL_H
13035
#define ADA_IMPLEMENTATION_INL_H
13036
13037
13038
13039
#include <cstddef>
13040
#include <span>
13041
#include <string>
13042
#include <string_view>
13043
#include <utility>
13044
#include <variant>
13045
#include <vector>
13046
13047
namespace ada {
13048
13049
#if ADA_INCLUDE_URL_PATTERN
13050
template <url_pattern_regex::regex_concept regex_provider>
13051
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
13052
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
13053
                  const std::string_view* base_url,
13054
                  const url_pattern_options* options) {
13055
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
13056
                                                        base_url, options);
13057
}
13058
13059
template <url_pattern_regex::regex_concept regex_provider>
13060
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
13061
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
13062
                       const std::string_view* base_url,
13063
                       const url_pattern_options* options) {
13064
  std::vector<std::string> processed;
13065
  processed.reserve(pathname_patterns.size());
13066
  for (const std::string_view pattern : pathname_patterns) {
13067
    if (base_url == nullptr) {
13068
      processed.emplace_back(pattern);
13069
      continue;
13070
    }
13071
    // With a base URL, each pattern is processed exactly as the pathname of
13072
    // a URLPatternInit would be: a relative pattern is resolved against the
13073
    // base URL's path (and an unparsable base URL is a type error).
13074
    url_pattern_init init{};
13075
    init.pathname = std::string(pattern);
13076
    init.base_url = std::string(*base_url);
13077
    auto result = url_pattern_init::process(
13078
        init, url_pattern_init::process_type::pattern);
13079
    if (!result) {
13080
      return tl::unexpected(result.error());
13081
    }
13082
    processed.push_back(std::move(result->pathname).value_or(std::string{}));
13083
  }
13084
  const bool ignore_case = options != nullptr && options->ignore_case;
13085
  auto list = url_pattern_list<regex_provider>::create(std::move(processed),
13086
                                                       ignore_case);
13087
  if (!list) {
13088
    return list;
13089
  }
13090
  // Routes outside the static/":param"/"*" subset are compiled as a
13091
  // URLPattern pathname component through the provider, with the same
13092
  // options parse_url_pattern would use (ignore_case included).
13093
  auto compile_options = url_pattern_compile_component_options::PATHNAME;
13094
  compile_options.ignore_case = ignore_case;
13095
  const url_pattern_list_detail::route_record* routes =
13096
      list->compiled_.template section<url_pattern_list_detail::route_record>(
13097
          list->compiled_.routes_offset);
13098
  for (size_t i = 0; i < list->patterns_.size(); i++) {
13099
    if (routes[i].regexp_component < 0) {
13100
      continue;
13101
    }
13102
    auto component = url_pattern_component<regex_provider>::compile(
13103
        list->patterns_[i], url_pattern_helpers::canonicalize_pathname,
13104
        compile_options);
13105
    if (!component) {
13106
      return tl::unexpected(component.error());
13107
    }
13108
    list->compiled_.group_names[i] = component->group_name_list;
13109
    list->regexp_components_.push_back(std::move(*component));
13110
  }
13111
  return list;
13112
}
13113
13114
template <url_pattern_regex::regex_concept regex_provider>
13115
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
13116
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns) {
13117
  std::vector<std::string> texts;
13118
  texts.reserve(patterns.size());
13119
  const bool ignore_case = !patterns.empty() && patterns[0].ignore_case();
13120
  for (const url_pattern<regex_provider>& pattern : patterns) {
13121
    if (pattern.ignore_case() != ignore_case) {
13122
      return tl::unexpected(errors::type_error);  // one flag per list
13123
    }
13124
    texts.emplace_back(pattern.get_pathname());
13125
  }
13126
  auto list =
13127
      url_pattern_list<regex_provider>::create(std::move(texts), ignore_case);
13128
  if (!list) {
13129
    return list;
13130
  }
13131
  // Routes outside the subset reuse the pattern's already compiled pathname
13132
  // component: no second create_instance through the provider.
13133
  const url_pattern_list_detail::route_record* routes =
13134
      list->compiled_.template section<url_pattern_list_detail::route_record>(
13135
          list->compiled_.routes_offset);
13136
  for (size_t i = 0; i < patterns.size(); i++) {
13137
    if (routes[i].regexp_component < 0) {
13138
      continue;
13139
    }
13140
    list->compiled_.group_names[i] =
13141
        patterns[i].pathname_component.group_name_list;
13142
    list->regexp_components_.push_back(patterns[i].pathname_component);
13143
  }
13144
  return list;
13145
}
13146
#endif  // ADA_INCLUDE_URL_PATTERN
13147
13148
}  // namespace ada
13149
13150
#endif  // ADA_IMPLEMENTATION_INL_H
13151
/* end file include/ada/implementation-inl.h */
13152
13153
#endif  // ADA_H
13154
/* end file include/ada.h */