Coverage Report

Created: 2026-09-28 06:13

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-09-19 20:22:55 -0400. Do not edit! */
2
/* begin file include/ada.h */
3
/**
4
 * @file ada.h
5
 * @brief Main header for the Ada URL parser library.
6
 *
7
 * This is the primary entry point for the Ada URL parser library. Including
8
 * this single header provides access to the complete Ada API, including:
9
 *
10
 * - URL parsing via `ada::parse()` function
11
 * - Two URL representations: `ada::url` and `ada::url_aggregator`
12
 * - URL search parameters via `ada::url_search_params`
13
 * - URL pattern matching via `ada::url_pattern` (URLPattern API)
14
 * - IDNA (Internationalized Domain Names) support
15
 *
16
 * @example
17
 * ```cpp
18
 *
19
 * // Parse a URL
20
 * auto url = ada::parse("https://example.com/path?query=1");
21
 * if (url) {
22
 *     std::cout << url->get_hostname(); // "example.com"
23
 * }
24
 * ```
25
 *
26
 * @see https://url.spec.whatwg.org/ - WHATWG URL Standard
27
 * @see https://github.com/ada-url/ada - Ada URL Parser GitHub Repository
28
 */
29
#ifndef ADA_H
30
#define ADA_H
31
32
/* begin file include/ada/ada_idna.h */
33
/* auto-generated on 2026-07-12 20:34:08 -0400. Do not edit! */
34
/* begin file include/idna.h */
35
#ifndef ADA_IDNA_H
36
#define ADA_IDNA_H
37
38
/* begin file include/ada/idna/unicode_transcoding.h */
39
#ifndef ADA_IDNA_UNICODE_TRANSCODING_H
40
#define ADA_IDNA_UNICODE_TRANSCODING_H
41
42
#include <string>
43
#include <string_view>
44
45
namespace ada::idna {
46
47
size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output);
48
49
size_t utf8_length_from_utf32(const char32_t* buf, size_t len);
50
51
size_t utf32_length_from_utf8(const char* buf, size_t len);
52
53
size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output);
54
55
}  // namespace ada::idna
56
57
#endif  // ADA_IDNA_UNICODE_TRANSCODING_H
58
/* end file include/ada/idna/unicode_transcoding.h */
59
/* begin file include/ada/idna/mapping.h */
60
#ifndef ADA_IDNA_MAPPING_H
61
#define ADA_IDNA_MAPPING_H
62
63
#include <string>
64
#include <string_view>
65
66
namespace ada::idna {
67
68
// If the input is ascii, then the mapping is just -> lower case.
69
void ascii_map(char* input, size_t length);
70
// Map the characters according to IDNA, returning the empty string on error.
71
std::u32string map(std::u32string_view input);
72
// Map into an existing buffer (cleared on entry). Returns false if any code
73
// point is disallowed. Reusing the buffer avoids repeated heap allocations
74
// when called in a loop over multiple labels.
75
bool map(std::u32string_view input, std::u32string& out);
76
77
}  // namespace ada::idna
78
79
#endif
80
/* end file include/ada/idna/mapping.h */
81
/* begin file include/ada/idna/normalization.h */
82
#ifndef ADA_IDNA_NORMALIZATION_H
83
#define ADA_IDNA_NORMALIZATION_H
84
85
#include <string>
86
#include <string_view>
87
88
namespace ada::idna {
89
90
// Returns true if `input` is already in Unicode Normalization Form C.
91
// Requires that internal tables have been loaded (call ensure via normalize
92
// or map first, or this returns false if tables are unavailable).
93
[[nodiscard]] bool is_already_nfc(std::u32string_view input) noexcept;
94
95
// Normalize the characters according to IDNA (Unicode Normalization Form C).
96
// Returns false if the internal Unicode tables could not be loaded; in that
97
// case `input` is left unchanged. Skips work when the string is already NFC.
98
[[nodiscard]] bool normalize(std::u32string& input);
99
100
}  // namespace ada::idna
101
#endif
102
/* end file include/ada/idna/normalization.h */
103
/* begin file include/ada/idna/punycode.h */
104
#ifndef ADA_IDNA_PUNYCODE_H
105
#define ADA_IDNA_PUNYCODE_H
106
107
#include <string>
108
#include <string_view>
109
110
namespace ada::idna {
111
112
bool punycode_to_utf32(std::string_view input, std::u32string& out);
113
bool verify_punycode(std::string_view input);
114
bool utf32_to_punycode(std::u32string_view input, std::string& out);
115
116
}  // namespace ada::idna
117
118
#endif  // ADA_IDNA_PUNYCODE_H
119
/* end file include/ada/idna/punycode.h */
120
/* begin file include/ada/idna/validity.h */
121
#ifndef ADA_IDNA_VALIDITY_H
122
#define ADA_IDNA_VALIDITY_H
123
124
#include <string>
125
#include <string_view>
126
127
namespace ada::idna {
128
129
/**
130
 * @see https://www.unicode.org/reports/tr46/#Validity_Criteria
131
 */
132
bool is_label_valid(std::u32string_view label);
133
134
}  // namespace ada::idna
135
136
#endif  // ADA_IDNA_VALIDITY_H
137
/* end file include/ada/idna/validity.h */
138
/* begin file include/ada/idna/to_ascii.h */
139
#ifndef ADA_IDNA_TO_ASCII_H
140
#define ADA_IDNA_TO_ASCII_H
141
142
#include <string>
143
#include <string_view>
144
145
/* begin file include/ada/idna/limits.h */
146
#ifndef ADA_IDNA_LIMITS_H
147
#define ADA_IDNA_LIMITS_H
148
149
#include <cstddef>
150
151
namespace ada::idna {
152
153
// Maximum accepted UTF-8 domain length for to_ascii / to_unicode.
154
// Bounds heap growth under untrusted input (DoS resistance). DNS wire limits
155
// are smaller; this allows long Unicode labels used in URL tests/fixtures.
156
inline constexpr size_t max_domain_input_bytes = 16384;
157
158
}  // namespace ada::idna
159
160
#endif  // ADA_IDNA_LIMITS_H
161
/* end file include/ada/idna/limits.h */
162
163
namespace ada::idna {
164
165
// Converts a domain (e.g., www.google.com) possibly containing international
166
// characters to an ascii domain (with punycode). It will not do percent
167
// decoding: percent decoding should be done prior to calling this function. We
168
// do not remove tabs and spaces, they should have been removed prior to calling
169
// this function. We also do not trim control characters. We also assume that
170
// the input is not empty. We return "" on error. Inputs longer than
171
// max_domain_input_bytes are rejected.
172
//
173
// This function may accept or even produce invalid domains (WHATWG carve-outs).
174
std::string to_ascii(std::string_view ut8_string);
175
176
// Same as to_ascii, but writes into `out` and returns false on error without
177
// relying on empty-string ambiguity.
178
[[nodiscard]] bool to_ascii(std::string_view ut8_string, std::string& out);
179
180
// Returns true if the string contains a forbidden code point according to the
181
// WHATGL URL specification:
182
// https://url.spec.whatwg.org/#forbidden-domain-code-point
183
bool contains_forbidden_domain_code_point(std::string_view ascii_string);
184
185
bool constexpr is_ascii(std::u32string_view view);
186
bool constexpr is_ascii(std::string_view view);
187
188
}  // namespace ada::idna
189
190
#endif  // ADA_IDNA_TO_ASCII_H
191
/* end file include/ada/idna/to_ascii.h */
192
/* begin file include/ada/idna/to_unicode.h */
193
#ifndef ADA_IDNA_TO_UNICODE_H
194
#define ADA_IDNA_TO_UNICODE_H
195
196
#include <string>
197
#include <string_view>
198
199
namespace ada::idna {
200
201
// UTS #46 ToUnicode. Never fails per the standard: on step failure the original
202
// label is kept. Inputs longer than max_domain_input_bytes are returned
203
// unchanged as a safety measure under untrusted input.
204
std::string to_unicode(std::string_view input);
205
206
// Writes into `out`. Returns false only if the input exceeds
207
// max_domain_input_bytes (out is left empty). Otherwise always returns true
208
// (ToUnicode does not fail).
209
[[nodiscard]] bool to_unicode(std::string_view input, std::string& out);
210
211
}  // namespace ada::idna
212
213
#endif  // ADA_IDNA_TO_UNICODE_H
214
/* end file include/ada/idna/to_unicode.h */
215
/* begin file include/ada/idna/identifier.h */
216
#ifndef ADA_IDNA_IDENTIFIER_H
217
#define ADA_IDNA_IDENTIFIER_H
218
219
#include <string>
220
#include <string_view>
221
222
namespace ada::idna {
223
224
// Verify if it is valid name code point given a Unicode code point and a
225
// boolean first: If first is true return the result of checking if code point
226
// is contained in the IdentifierStart set of code points. Otherwise return the
227
// result of checking if code point is contained in the IdentifierPart set of
228
// code points. Returns false if the input is empty or the code point is not
229
// valid. There is minimal Unicode error handling: the input should be valid
230
// UTF-8. https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point
231
bool valid_name_code_point(char32_t code_point, bool first);
232
233
}  // namespace ada::idna
234
235
#endif
236
/* end file include/ada/idna/identifier.h */
237
238
#endif
239
/* end file include/idna.h */
240
/* end file include/ada/ada_idna.h */
241
/* begin file include/ada/character_sets.h */
242
/**
243
 * @file character_sets.h
244
 * @brief Declaration of the character sets used by unicode functions.
245
 * @author Node.js
246
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
247
 */
248
#ifndef ADA_CHARACTER_SETS_H
249
#define ADA_CHARACTER_SETS_H
250
251
/* begin file include/ada/common_defs.h */
252
/**
253
 * @file common_defs.h
254
 * @brief Cross-platform compiler macros and common definitions.
255
 *
256
 * This header provides compiler-specific macros for optimization hints,
257
 * platform detection, SIMD support detection, and development/debug utilities.
258
 * It ensures consistent behavior across different compilers (GCC, Clang, MSVC).
259
 */
260
#ifndef ADA_COMMON_DEFS_H
261
#define ADA_COMMON_DEFS_H
262
263
// https://en.cppreference.com/w/cpp/feature_test#Library_features
264
// detect C++20 features
265
#include <version>
266
267
#ifdef _MSC_VER
268
#define ADA_VISUAL_STUDIO 1
269
/**
270
 * We want to differentiate carefully between
271
 * clang under visual studio and regular visual
272
 * studio.
273
 */
274
#ifdef __clang__
275
// clang under visual studio
276
#define ADA_CLANG_VISUAL_STUDIO 1
277
#else
278
// just regular visual studio (best guess)
279
#define ADA_REGULAR_VISUAL_STUDIO 1
280
#endif  // __clang__
281
#endif  // _MSC_VER
282
283
#if defined(__GNUC__)
284
// Marks a block with a name so that MCA analysis can see it.
285
#define ADA_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
286
#define ADA_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
287
#define ADA_DEBUG_BLOCK(name, block) \
288
  BEGIN_DEBUG_BLOCK(name);           \
289
  block;                             \
290
  END_DEBUG_BLOCK(name);
291
#else
292
#define ADA_BEGIN_DEBUG_BLOCK(name)
293
#define ADA_END_DEBUG_BLOCK(name)
294
#define ADA_DEBUG_BLOCK(name, block)
295
#endif
296
297
// Align to N-byte boundary
298
#define ADA_ROUNDUP_N(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
299
#define ADA_ROUNDDOWN_N(a, n) ((a) & ~((n) - 1))
300
301
#define ADA_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n) - 1)) == 0)
302
303
#if defined(ADA_REGULAR_VISUAL_STUDIO)
304
305
#define ada_really_inline __forceinline
306
#define ada_never_inline __declspec(noinline)
307
308
#define ada_unused
309
#define ada_warn_unused
310
311
#define ADA_PUSH_DISABLE_WARNINGS __pragma(warning(push))
312
#define ADA_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0))
313
#define ADA_DISABLE_VS_WARNING(WARNING_NUMBER) \
314
  __pragma(warning(disable : WARNING_NUMBER))
315
// Get rid of Intellisense-only warnings (Code Analysis)
316
// Though __has_include is C++17, it is supported in Visual Studio 2017 or
317
// better (_MSC_VER>=1910).
318
#ifdef __has_include
319
#if __has_include(<CppCoreCheck\Warnings.h>)
320
#include <CppCoreCheck\Warnings.h>
321
#define ADA_DISABLE_UNDESIRED_WARNINGS \
322
  ADA_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
323
#endif
324
#endif
325
326
#ifndef ADA_DISABLE_UNDESIRED_WARNINGS
327
#define ADA_DISABLE_UNDESIRED_WARNINGS
328
#endif
329
330
#define ADA_DISABLE_DEPRECATED_WARNING ADA_DISABLE_VS_WARNING(4996)
331
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING
332
#define ADA_POP_DISABLE_WARNINGS __pragma(warning(pop))
333
334
#else  // ADA_REGULAR_VISUAL_STUDIO
335
336
#define ada_really_inline inline __attribute__((always_inline))
337
#define ada_never_inline inline __attribute__((noinline))
338
339
#define ada_unused __attribute__((unused))
340
#define ada_warn_unused __attribute__((warn_unused_result))
341
342
#define ADA_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
343
// gcc doesn't seem to disable all warnings with all and extra, add warnings
344
// here as necessary
345
#define ADA_PUSH_DISABLE_ALL_WARNINGS               \
346
  ADA_PUSH_DISABLE_WARNINGS                         \
347
  ADA_DISABLE_GCC_WARNING("-Weffc++")               \
348
  ADA_DISABLE_GCC_WARNING("-Wall")                  \
349
  ADA_DISABLE_GCC_WARNING("-Wconversion")           \
350
  ADA_DISABLE_GCC_WARNING("-Wextra")                \
351
  ADA_DISABLE_GCC_WARNING("-Wattributes")           \
352
  ADA_DISABLE_GCC_WARNING("-Wimplicit-fallthrough") \
353
  ADA_DISABLE_GCC_WARNING("-Wnon-virtual-dtor")     \
354
  ADA_DISABLE_GCC_WARNING("-Wreturn-type")          \
355
  ADA_DISABLE_GCC_WARNING("-Wshadow")               \
356
  ADA_DISABLE_GCC_WARNING("-Wunused-parameter")     \
357
  ADA_DISABLE_GCC_WARNING("-Wunused-variable")      \
358
  ADA_DISABLE_GCC_WARNING("-Wsign-compare")
359
#define ADA_PRAGMA(P) _Pragma(#P)
360
#define ADA_DISABLE_GCC_WARNING(WARNING) \
361
  ADA_PRAGMA(GCC diagnostic ignored WARNING)
362
#if defined(ADA_CLANG_VISUAL_STUDIO)
363
#define ADA_DISABLE_UNDESIRED_WARNINGS \
364
  ADA_DISABLE_GCC_WARNING("-Wmicrosoft-include")
365
#else
366
#define ADA_DISABLE_UNDESIRED_WARNINGS
367
#endif
368
#define ADA_DISABLE_DEPRECATED_WARNING \
369
  ADA_DISABLE_GCC_WARNING("-Wdeprecated-declarations")
370
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING \
371
  ADA_DISABLE_GCC_WARNING("-Wstrict-overflow")
372
#define ADA_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
373
374
#endif  // MSC_VER
375
376
#if defined(ADA_VISUAL_STUDIO)
377
/**
378
 * It does not matter here whether you are using
379
 * the regular visual studio or clang under visual
380
 * studio.
381
 */
382
#if ADA_USING_LIBRARY
383
#define ADA_DLLIMPORTEXPORT __declspec(dllimport)
384
#else
385
#define ADA_DLLIMPORTEXPORT __declspec(dllexport)
386
#endif
387
#else
388
#define ADA_DLLIMPORTEXPORT
389
#endif
390
391
/// If EXPR is an error, returns it.
392
#define ADA_TRY(EXPR)   \
393
  {                     \
394
    auto _err = (EXPR); \
395
    if (_err) {         \
396
      return _err;      \
397
    }                   \
398
  }
399
400
// __has_cpp_attribute is part of C++20
401
#if !defined(__has_cpp_attribute)
402
#define __has_cpp_attribute(x) 0
403
#endif
404
405
#if __has_cpp_attribute(gnu::noinline)
406
#define ADA_ATTRIBUTE_NOINLINE [[gnu::noinline]]
407
#else
408
#define ADA_ATTRIBUTE_NOINLINE
409
#endif
410
411
namespace ada {
412
0
[[noreturn]] inline void unreachable() {
413
0
#ifdef __GNUC__
414
0
  __builtin_unreachable();
415
#elif defined(_MSC_VER)
416
  __assume(false);
417
#else
418
#endif
419
0
}
420
}  // namespace ada
421
422
// Unless the programmer has already set ADA_DEVELOPMENT_CHECKS,
423
// we want to set it under debug builds. We detect a debug build
424
// under Visual Studio when the _DEBUG macro is set. Under the other
425
// compilers, we use the fact that they define __OPTIMIZE__ whenever
426
// they allow optimizations.
427
// It is possible that this could miss some cases where ADA_DEVELOPMENT_CHECKS
428
// is helpful, but the programmer can set the macro ADA_DEVELOPMENT_CHECKS.
429
// It could also wrongly set ADA_DEVELOPMENT_CHECKS (e.g., if the programmer
430
// sets _DEBUG in a release build under Visual Studio, or if some compiler fails
431
// to set the __OPTIMIZE__ macro).
432
#if !defined(ADA_DEVELOPMENT_CHECKS) && !defined(NDEBUG)
433
#ifdef _MSC_VER
434
// Visual Studio seems to set _DEBUG for debug builds.
435
#ifdef _DEBUG
436
#define ADA_DEVELOPMENT_CHECKS 1
437
#endif  // _DEBUG
438
#else   // _MSC_VER
439
// All other compilers appear to set __OPTIMIZE__ to a positive integer
440
// when the compiler is optimizing.
441
#ifndef __OPTIMIZE__
442
#define ADA_DEVELOPMENT_CHECKS 1
443
#endif  // __OPTIMIZE__
444
#endif  // _MSC_VER
445
#endif  // ADA_DEVELOPMENT_CHECKS
446
447
#define ADA_STR(x) #x
448
449
#if ADA_DEVELOPMENT_CHECKS
450
#define ADA_REQUIRE(EXPR) \
451
  {                       \
452
    if (!(EXPR) { abort(); }) }
453
454
#define ADA_FAIL(MESSAGE)                            \
455
  do {                                               \
456
    std::cerr << "FAIL: " << (MESSAGE) << std::endl; \
457
    abort();                                         \
458
  } while (0);
459
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)                                    \
460
  do {                                                                         \
461
    if (LHS != RHS) {                                                          \
462
      std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \
463
      ADA_FAIL(MESSAGE);                                                       \
464
    }                                                                          \
465
  } while (0);
466
#define ADA_ASSERT_TRUE(COND)                                               \
467
  do {                                                                      \
468
    if (!(COND)) {                                                          \
469
      std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \
470
                << std::endl;                                               \
471
      ADA_FAIL(ADA_STR(COND));                                              \
472
    }                                                                       \
473
  } while (0);
474
#else
475
#define ADA_FAIL(MESSAGE)
476
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)
477
#define ADA_ASSERT_TRUE(COND)
478
#endif
479
480
#ifdef ADA_VISUAL_STUDIO
481
#define ADA_ASSUME(COND) __assume(COND)
482
#else
483
#define ADA_ASSUME(COND)       \
484
  do {                         \
485
    if (!(COND)) {             \
486
      __builtin_unreachable(); \
487
    }                          \
488
  } while (0)
489
#endif
490
491
#if defined(__SSSE3__)
492
#define ADA_SSSE3 1
493
#endif
494
495
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
496
    (defined(_M_AMD64) || defined(_M_X64) ||                         \
497
     (defined(_M_IX86_FP) && _M_IX86_FP == 2))
498
#define ADA_SSE2 1
499
#endif
500
501
// AVX-512 byte/word ops + 128/256-bit vectors of AVX-512 instructions.
502
// Used for optional high-performance IP address parsing kernels.
503
#if defined(__AVX512BW__) && defined(__AVX512VL__)
504
#define ADA_AVX512 1
505
#endif
506
507
#if defined(__aarch64__) || defined(_M_ARM64)
508
#define ADA_NEON 1
509
#endif
510
511
#if defined(__loongarch_sx)
512
#define ADA_LSX 1
513
#endif
514
515
#if defined(__riscv_v) && __riscv_v_intrinsic >= 11000
516
// Support RVV intrinsics v0.11 and above
517
#define ADA_RVV 1
518
#endif
519
520
#ifndef __has_cpp_attribute
521
#define ada_lifetime_bound
522
#elif __has_cpp_attribute(msvc::lifetimebound)
523
#define ada_lifetime_bound [[msvc::lifetimebound]]
524
#elif __has_cpp_attribute(clang::lifetimebound)
525
#define ada_lifetime_bound [[clang::lifetimebound]]
526
#elif __has_cpp_attribute(lifetimebound)
527
#define ada_lifetime_bound [[lifetimebound]]
528
#else
529
#define ada_lifetime_bound
530
#endif
531
532
#ifdef __cpp_lib_format
533
#if __cpp_lib_format >= 202110L
534
#include <format>
535
#define ADA_HAS_FORMAT 1
536
#endif
537
#endif
538
539
#ifndef ADA_INCLUDE_URL_PATTERN
540
#define ADA_INCLUDE_URL_PATTERN 1
541
#endif  // ADA_INCLUDE_URL_PATTERN
542
543
// When set, NEON all-zero tests use an integer compare instead of
544
// reinterpreting the bits as a double. The double compare is faster (no
545
// GPR transfer) but is incorrect if flush-to-zero is enabled.
546
#ifndef ADA_NEON_SAFE_ZERO_CHECK
547
#define ADA_NEON_SAFE_ZERO_CHECK 0
548
#endif  // ADA_NEON_SAFE_ZERO_CHECK
549
550
#endif  // ADA_COMMON_DEFS_H
551
/* end file include/ada/common_defs.h */
552
#include <cstdint>
553
554
/**
555
 * These functions are not part of our public API and may
556
 * change at any time.
557
 * @private
558
 * @namespace ada::character_sets
559
 * @brief Includes the definitions for unicode character sets.
560
 */
561
namespace ada::character_sets {
562
ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i);
563
}  // namespace ada::character_sets
564
565
#endif  // ADA_CHARACTER_SETS_H
566
/* end file include/ada/character_sets.h */
567
/* begin file include/ada/character_sets-inl.h */
568
/**
569
 * @file character_sets-inl.h
570
 * @brief Definitions of the character sets used by unicode functions.
571
 * @author Node.js
572
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
573
 */
574
#ifndef ADA_CHARACTER_SETS_INL_H
575
#define ADA_CHARACTER_SETS_INL_H
576
577
578
/**
579
 * These functions are not part of our public API and may
580
 * change at any time.
581
 * @private
582
 */
583
namespace ada::character_sets {
584
585
constexpr char hex[1024] =
586
    "%00\0%01\0%02\0%03\0%04\0%05\0%06\0%07\0"
587
    "%08\0%09\0%0A\0%0B\0%0C\0%0D\0%0E\0%0F\0"
588
    "%10\0%11\0%12\0%13\0%14\0%15\0%16\0%17\0"
589
    "%18\0%19\0%1A\0%1B\0%1C\0%1D\0%1E\0%1F\0"
590
    "%20\0%21\0%22\0%23\0%24\0%25\0%26\0%27\0"
591
    "%28\0%29\0%2A\0%2B\0%2C\0%2D\0%2E\0%2F\0"
592
    "%30\0%31\0%32\0%33\0%34\0%35\0%36\0%37\0"
593
    "%38\0%39\0%3A\0%3B\0%3C\0%3D\0%3E\0%3F\0"
594
    "%40\0%41\0%42\0%43\0%44\0%45\0%46\0%47\0"
595
    "%48\0%49\0%4A\0%4B\0%4C\0%4D\0%4E\0%4F\0"
596
    "%50\0%51\0%52\0%53\0%54\0%55\0%56\0%57\0"
597
    "%58\0%59\0%5A\0%5B\0%5C\0%5D\0%5E\0%5F\0"
598
    "%60\0%61\0%62\0%63\0%64\0%65\0%66\0%67\0"
599
    "%68\0%69\0%6A\0%6B\0%6C\0%6D\0%6E\0%6F\0"
600
    "%70\0%71\0%72\0%73\0%74\0%75\0%76\0%77\0"
601
    "%78\0%79\0%7A\0%7B\0%7C\0%7D\0%7E\0%7F\0"
602
    "%80\0%81\0%82\0%83\0%84\0%85\0%86\0%87\0"
603
    "%88\0%89\0%8A\0%8B\0%8C\0%8D\0%8E\0%8F\0"
604
    "%90\0%91\0%92\0%93\0%94\0%95\0%96\0%97\0"
605
    "%98\0%99\0%9A\0%9B\0%9C\0%9D\0%9E\0%9F\0"
606
    "%A0\0%A1\0%A2\0%A3\0%A4\0%A5\0%A6\0%A7\0"
607
    "%A8\0%A9\0%AA\0%AB\0%AC\0%AD\0%AE\0%AF\0"
608
    "%B0\0%B1\0%B2\0%B3\0%B4\0%B5\0%B6\0%B7\0"
609
    "%B8\0%B9\0%BA\0%BB\0%BC\0%BD\0%BE\0%BF\0"
610
    "%C0\0%C1\0%C2\0%C3\0%C4\0%C5\0%C6\0%C7\0"
611
    "%C8\0%C9\0%CA\0%CB\0%CC\0%CD\0%CE\0%CF\0"
612
    "%D0\0%D1\0%D2\0%D3\0%D4\0%D5\0%D6\0%D7\0"
613
    "%D8\0%D9\0%DA\0%DB\0%DC\0%DD\0%DE\0%DF\0"
614
    "%E0\0%E1\0%E2\0%E3\0%E4\0%E5\0%E6\0%E7\0"
615
    "%E8\0%E9\0%EA\0%EB\0%EC\0%ED\0%EE\0%EF\0"
616
    "%F0\0%F1\0%F2\0%F3\0%F4\0%F5\0%F6\0%F7\0"
617
    "%F8\0%F9\0%FA\0%FB\0%FC\0%FD\0%FE\0%FF";
618
619
constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32] = {
620
    // 00     01     02     03     04     05     06     07
621
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
622
    // 08     09     0A     0B     0C     0D     0E     0F
623
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
624
    // 10     11     12     13     14     15     16     17
625
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
626
    // 18     19     1A     1B     1C     1D     1E     1F
627
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
628
    // 20     21     22     23     24     25     26     27
629
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
630
    // 28     29     2A     2B     2C     2D     2E     2F
631
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
632
    // 30     31     32     33     34     35     36     37
633
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
634
    // 38     39     3A     3B     3C     3D     3E     3F
635
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
636
    // 40     41     42     43     44     45     46     47
637
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
638
    // 48     49     4A     4B     4C     4D     4E     4F
639
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
640
    // 50     51     52     53     54     55     56     57
641
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
642
    // 58     59     5A     5B     5C     5D     5E     5F
643
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
644
    // 60     61     62     63     64     65     66     67
645
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
646
    // 68     69     6A     6B     6C     6D     6E     6F
647
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
648
    // 70     71     72     73     74     75     76     77
649
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
650
    // 78     79     7A     7B     7C     7D     7E     7F
651
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
652
    // 80     81     82     83     84     85     86     87
653
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
654
    // 88     89     8A     8B     8C     8D     8E     8F
655
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
656
    // 90     91     92     93     94     95     96     97
657
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
658
    // 98     99     9A     9B     9C     9D     9E     9F
659
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
660
    // A0     A1     A2     A3     A4     A5     A6     A7
661
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
662
    // A8     A9     AA     AB     AC     AD     AE     AF
663
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
664
    // B0     B1     B2     B3     B4     B5     B6     B7
665
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
666
    // B8     B9     BA     BB     BC     BD     BE     BF
667
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
668
    // C0     C1     C2     C3     C4     C5     C6     C7
669
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
670
    // C8     C9     CA     CB     CC     CD     CE     CF
671
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
672
    // D0     D1     D2     D3     D4     D5     D6     D7
673
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
674
    // D8     D9     DA     DB     DC     DD     DE     DF
675
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
676
    // E0     E1     E2     E3     E4     E5     E6     E7
677
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
678
    // E8     E9     EA     EB     EC     ED     EE     EF
679
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
680
    // F0     F1     F2     F3     F4     F5     F6     F7
681
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
682
    // F8     F9     FA     FB     FC     FD     FE     FF
683
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
684
685
constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32] = {
686
    // 00     01     02     03     04     05     06     07
687
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
688
    // 08     09     0A     0B     0C     0D     0E     0F
689
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
690
    // 10     11     12     13     14     15     16     17
691
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
692
    // 18     19     1A     1B     1C     1D     1E     1F
693
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
694
    // 20     21     22     23     24     25     26     27
695
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x80,
696
    // 28     29     2A     2B     2C     2D     2E     2F
697
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
698
    // 30     31     32     33     34     35     36     37
699
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
700
    // 38     39     3A     3B     3C     3D     3E     3F
701
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
702
    // 40     41     42     43     44     45     46     47
703
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
704
    // 48     49     4A     4B     4C     4D     4E     4F
705
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
706
    // 50     51     52     53     54     55     56     57
707
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
708
    // 58     59     5A     5B     5C     5D     5E     5F
709
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
710
    // 60     61     62     63     64     65     66     67
711
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
712
    // 68     69     6A     6B     6C     6D     6E     6F
713
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
714
    // 70     71     72     73     74     75     76     77
715
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
716
    // 78     79     7A     7B     7C     7D     7E     7F
717
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
718
    // 80     81     82     83     84     85     86     87
719
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
720
    // 88     89     8A     8B     8C     8D     8E     8F
721
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
722
    // 90     91     92     93     94     95     96     97
723
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
724
    // 98     99     9A     9B     9C     9D     9E     9F
725
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
726
    // A0     A1     A2     A3     A4     A5     A6     A7
727
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
728
    // A8     A9     AA     AB     AC     AD     AE     AF
729
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
730
    // B0     B1     B2     B3     B4     B5     B6     B7
731
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
732
    // B8     B9     BA     BB     BC     BD     BE     BF
733
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
734
    // C0     C1     C2     C3     C4     C5     C6     C7
735
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
736
    // C8     C9     CA     CB     CC     CD     CE     CF
737
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
738
    // D0     D1     D2     D3     D4     D5     D6     D7
739
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
740
    // D8     D9     DA     DB     DC     DD     DE     DF
741
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
742
    // E0     E1     E2     E3     E4     E5     E6     E7
743
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
744
    // E8     E9     EA     EB     EC     ED     EE     EF
745
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
746
    // F0     F1     F2     F3     F4     F5     F6     F7
747
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
748
    // F8     F9     FA     FB     FC     FD     FE     FF
749
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
750
751
constexpr uint8_t QUERY_PERCENT_ENCODE[32] = {
752
    // 00     01     02     03     04     05     06     07
753
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
754
    // 08     09     0A     0B     0C     0D     0E     0F
755
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
756
    // 10     11     12     13     14     15     16     17
757
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
758
    // 18     19     1A     1B     1C     1D     1E     1F
759
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
760
    // 20     21     22     23     24     25     26     27
761
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
762
    // 28     29     2A     2B     2C     2D     2E     2F
763
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
764
    // 30     31     32     33     34     35     36     37
765
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
766
    // 38     39     3A     3B     3C     3D     3E     3F
767
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
768
    // 40     41     42     43     44     45     46     47
769
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
770
    // 48     49     4A     4B     4C     4D     4E     4F
771
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
772
    // 50     51     52     53     54     55     56     57
773
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
774
    // 58     59     5A     5B     5C     5D     5E     5F
775
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
776
    // 60     61     62     63     64     65     66     67
777
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
778
    // 68     69     6A     6B     6C     6D     6E     6F
779
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
780
    // 70     71     72     73     74     75     76     77
781
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
782
    // 78     79     7A     7B     7C     7D     7E     7F
783
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
784
    // 80     81     82     83     84     85     86     87
785
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
786
    // 88     89     8A     8B     8C     8D     8E     8F
787
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
788
    // 90     91     92     93     94     95     96     97
789
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
790
    // 98     99     9A     9B     9C     9D     9E     9F
791
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
792
    // A0     A1     A2     A3     A4     A5     A6     A7
793
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
794
    // A8     A9     AA     AB     AC     AD     AE     AF
795
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
796
    // B0     B1     B2     B3     B4     B5     B6     B7
797
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
798
    // B8     B9     BA     BB     BC     BD     BE     BF
799
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
800
    // C0     C1     C2     C3     C4     C5     C6     C7
801
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
802
    // C8     C9     CA     CB     CC     CD     CE     CF
803
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
804
    // D0     D1     D2     D3     D4     D5     D6     D7
805
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
806
    // D8     D9     DA     DB     DC     DD     DE     DF
807
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
808
    // E0     E1     E2     E3     E4     E5     E6     E7
809
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
810
    // E8     E9     EA     EB     EC     ED     EE     EF
811
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
812
    // F0     F1     F2     F3     F4     F5     F6     F7
813
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
814
    // F8     F9     FA     FB     FC     FD     FE     FF
815
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
816
817
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32] = {
818
    // 00     01     02     03     04     05     06     07
819
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
820
    // 08     09     0A     0B     0C     0D     0E     0F
821
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
822
    // 10     11     12     13     14     15     16     17
823
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
824
    // 18     19     1A     1B     1C     1D     1E     1F
825
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
826
    // 20     21     22     23     24     25     26     27
827
    0x01 | 0x00 | 0x04 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
828
    // 28     29     2A     2B     2C     2D     2E     2F
829
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
830
    // 30     31     32     33     34     35     36     37
831
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
832
    // 38     39     3A     3B     3C     3D     3E     3F
833
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
834
    // 40     41     42     43     44     45     46     47
835
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
836
    // 48     49     4A     4B     4C     4D     4E     4F
837
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
838
    // 50     51     52     53     54     55     56     57
839
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
840
    // 58     59     5A     5B     5C     5D     5E     5F
841
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
842
    // 60     61     62     63     64     65     66     67
843
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
844
    // 68     69     6A     6B     6C     6D     6E     6F
845
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
846
    // 70     71     72     73     74     75     76     77
847
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
848
    // 78     79     7A     7B     7C     7D     7E     7F
849
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
850
    // 80     81     82     83     84     85     86     87
851
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
852
    // 88     89     8A     8B     8C     8D     8E     8F
853
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
854
    // 90     91     92     93     94     95     96     97
855
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
856
    // 98     99     9A     9B     9C     9D     9E     9F
857
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
858
    // A0     A1     A2     A3     A4     A5     A6     A7
859
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
860
    // A8     A9     AA     AB     AC     AD     AE     AF
861
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
862
    // B0     B1     B2     B3     B4     B5     B6     B7
863
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
864
    // B8     B9     BA     BB     BC     BD     BE     BF
865
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
866
    // C0     C1     C2     C3     C4     C5     C6     C7
867
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
868
    // C8     C9     CA     CB     CC     CD     CE     CF
869
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
870
    // D0     D1     D2     D3     D4     D5     D6     D7
871
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
872
    // D8     D9     DA     DB     DC     DD     DE     DF
873
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
874
    // E0     E1     E2     E3     E4     E5     E6     E7
875
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
876
    // E8     E9     EA     EB     EC     ED     EE     EF
877
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
878
    // F0     F1     F2     F3     F4     F5     F6     F7
879
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
880
    // F8     F9     FA     FB     FC     FD     FE     FF
881
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
882
883
constexpr uint8_t USERINFO_PERCENT_ENCODE[32] = {
884
    // 00     01     02     03     04     05     06     07
885
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
886
    // 08     09     0A     0B     0C     0D     0E     0F
887
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
888
    // 10     11     12     13     14     15     16     17
889
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
890
    // 18     19     1A     1B     1C     1D     1E     1F
891
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
892
    // 20     21     22     23     24     25     26     27
893
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
894
    // 28     29     2A     2B     2C     2D     2E     2F
895
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
896
    // 30     31     32     33     34     35     36     37
897
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
898
    // 38     39     3A     3B     3C     3D     3E     3F
899
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
900
    // 40     41     42     43     44     45     46     47
901
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
902
    // 48     49     4A     4B     4C     4D     4E     4F
903
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
904
    // 50     51     52     53     54     55     56     57
905
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
906
    // 58     59     5A     5B     5C     5D     5E     5F
907
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
908
    // 60     61     62     63     64     65     66     67
909
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
910
    // 68     69     6A     6B     6C     6D     6E     6F
911
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
912
    // 70     71     72     73     74     75     76     77
913
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
914
    // 78     79     7A     7B     7C     7D     7E     7F
915
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80,
916
    // 80     81     82     83     84     85     86     87
917
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
918
    // 88     89     8A     8B     8C     8D     8E     8F
919
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
920
    // 90     91     92     93     94     95     96     97
921
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
922
    // 98     99     9A     9B     9C     9D     9E     9F
923
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
924
    // A0     A1     A2     A3     A4     A5     A6     A7
925
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
926
    // A8     A9     AA     AB     AC     AD     AE     AF
927
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
928
    // B0     B1     B2     B3     B4     B5     B6     B7
929
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
930
    // B8     B9     BA     BB     BC     BD     BE     BF
931
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
932
    // C0     C1     C2     C3     C4     C5     C6     C7
933
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
934
    // C8     C9     CA     CB     CC     CD     CE     CF
935
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
936
    // D0     D1     D2     D3     D4     D5     D6     D7
937
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
938
    // D8     D9     DA     DB     DC     DD     DE     DF
939
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
940
    // E0     E1     E2     E3     E4     E5     E6     E7
941
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
942
    // E8     E9     EA     EB     EC     ED     EE     EF
943
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
944
    // F0     F1     F2     F3     F4     F5     F6     F7
945
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
946
    // F8     F9     FA     FB     FC     FD     FE     FF
947
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
948
949
constexpr uint8_t PATH_PERCENT_ENCODE[32] = {
950
    // 00     01     02     03     04     05     06     07
951
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
952
    // 08     09     0A     0B     0C     0D     0E     0F
953
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
954
    // 10     11     12     13     14     15     16     17
955
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
956
    // 18     19     1A     1B     1C     1D     1E     1F
957
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
958
    // 20     21     22     23     24     25     26     27
959
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
960
    // 28     29     2A     2B     2C     2D     2E     2F
961
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
962
    // 30     31     32     33     34     35     36     37
963
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
964
    // 38     39     3A     3B     3C     3D     3E     3F
965
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
966
    // 40     41     42     43     44     45     46     47
967
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
968
    // 48     49     4A     4B     4C     4D     4E     4F
969
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
970
    // 50     51     52     53     54     55     56     57
971
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
972
    // 58     59     5A     5B     5C     5D     5E     5F
973
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x40 | 0x00,
974
    // 60     61     62     63     64     65     66     67
975
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
976
    // 68     69     6A     6B     6C     6D     6E     6F
977
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
978
    // 70     71     72     73     74     75     76     77
979
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
980
    // 78     79     7A     7B     7C     7D     7E     7F
981
    0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80,
982
    // 80     81     82     83     84     85     86     87
983
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
984
    // 88     89     8A     8B     8C     8D     8E     8F
985
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
986
    // 90     91     92     93     94     95     96     97
987
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
988
    // 98     99     9A     9B     9C     9D     9E     9F
989
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
990
    // A0     A1     A2     A3     A4     A5     A6     A7
991
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
992
    // A8     A9     AA     AB     AC     AD     AE     AF
993
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
994
    // B0     B1     B2     B3     B4     B5     B6     B7
995
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
996
    // B8     B9     BA     BB     BC     BD     BE     BF
997
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
998
    // C0     C1     C2     C3     C4     C5     C6     C7
999
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1000
    // C8     C9     CA     CB     CC     CD     CE     CF
1001
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1002
    // D0     D1     D2     D3     D4     D5     D6     D7
1003
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1004
    // D8     D9     DA     DB     DC     DD     DE     DF
1005
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1006
    // E0     E1     E2     E3     E4     E5     E6     E7
1007
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1008
    // E8     E9     EA     EB     EC     ED     EE     EF
1009
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1010
    // F0     F1     F2     F3     F4     F5     F6     F7
1011
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1012
    // F8     F9     FA     FB     FC     FD     FE     FF
1013
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1014
1015
constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = {
1016
    // 00     01     02     03     04     05     06     07
1017
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1018
    // 08     09     0A     0B     0C     0D     0E     0F
1019
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1020
    // 10     11     12     13     14     15     16     17
1021
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1022
    // 18     19     1A     1B     1C     1D     1E     1F
1023
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1024
    // 20     21     22     23     24     25     26     27
1025
    0x00 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1026
    // 28     29     2A     2B     2C     2D     2E     2F
1027
    0x01 | 0x02 | 0x00 | 0x08 | 0x10 | 0x00 | 0x00 | 0x80,
1028
    // 30     31     32     33     34     35     36     37
1029
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1030
    // 38     39     3A     3B     3C     3D     3E     3F
1031
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1032
    // 40     41     42     43     44     45     46     47
1033
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1034
    // 48     49     4A     4B     4C     4D     4E     4F
1035
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1036
    // 50     51     52     53     54     55     56     57
1037
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1038
    // 58     59     5A     5B     5C     5D     5E     5F
1039
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
1040
    // 60     61     62     63     64     65     66     67
1041
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1042
    // 68     69     6A     6B     6C     6D     6E     6F
1043
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1044
    // 70     71     72     73     74     75     76     77
1045
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1046
    // 78     79     7A     7B     7C     7D     7E     7F
1047
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1048
    // 80     81     82     83     84     85     86     87
1049
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1050
    // 88     89     8A     8B     8C     8D     8E     8F
1051
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1052
    // 90     91     92     93     94     95     96     97
1053
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1054
    // 98     99     9A     9B     9C     9D     9E     9F
1055
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1056
    // A0     A1     A2     A3     A4     A5     A6     A7
1057
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1058
    // A8     A9     AA     AB     AC     AD     AE     AF
1059
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1060
    // B0     B1     B2     B3     B4     B5     B6     B7
1061
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1062
    // B8     B9     BA     BB     BC     BD     BE     BF
1063
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1064
    // C0     C1     C2     C3     C4     C5     C6     C7
1065
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1066
    // C8     C9     CA     CB     CC     CD     CE     CF
1067
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1068
    // D0     D1     D2     D3     D4     D5     D6     D7
1069
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1070
    // D8     D9     DA     DB     DC     DD     DE     DF
1071
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1072
    // E0     E1     E2     E3     E4     E5     E6     E7
1073
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1074
    // E8     E9     EA     EB     EC     ED     EE     EF
1075
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1076
    // F0     F1     F2     F3     F4     F5     F6     F7
1077
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1078
    // F8     F9     FA     FB     FC     FD     FE     FF
1079
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1080
1081
7.87M
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1082
7.87M
  return !!(a[i >> 3] & (1 << (i & 7)));
1083
7.87M
}
1084
1085
}  // namespace ada::character_sets
1086
1087
#endif  // ADA_CHARACTER_SETS_INL_H
1088
/* end file include/ada/character_sets-inl.h */
1089
/* begin file include/ada/checkers-inl.h */
1090
/**
1091
 * @file checkers-inl.h
1092
 * @brief Definitions for URL specific checkers used within Ada.
1093
 */
1094
#ifndef ADA_CHECKERS_INL_H
1095
#define ADA_CHECKERS_INL_H
1096
1097
#include <bit>
1098
#include <cstdint>
1099
#include <string_view>
1100
/* begin file include/ada/checkers.h */
1101
/**
1102
 * @file checkers.h
1103
 * @brief Declarations for URL specific checkers used within Ada.
1104
 */
1105
#ifndef ADA_CHECKERS_H
1106
#define ADA_CHECKERS_H
1107
1108
1109
#include <cstring>
1110
#include <string_view>
1111
1112
/**
1113
 * These functions are not part of our public API and may
1114
 * change at any time.
1115
 * @private
1116
 * @namespace ada::checkers
1117
 * @brief Includes the definitions for validation functions
1118
 */
1119
namespace ada::checkers {
1120
1121
/**
1122
 * @private
1123
 * Assuming that x is an ASCII letter, this function returns the lower case
1124
 * equivalent.
1125
 * @details More likely to be inlined by the compiler and constexpr.
1126
 */
1127
constexpr char to_lower(char x) noexcept;
1128
1129
/**
1130
 * @private
1131
 * Returns true if the character is an ASCII letter. Equivalent to std::isalpha
1132
 * but more likely to be inlined by the compiler.
1133
 *
1134
 * @attention std::isalpha is not constexpr generally.
1135
 */
1136
constexpr bool is_alpha(char x) noexcept;
1137
1138
/**
1139
 * @private
1140
 * Check whether x is an ASCII digit. More likely to be inlined than
1141
 * std::isdigit.
1142
 */
1143
constexpr bool is_digit(char x) noexcept;
1144
1145
/**
1146
 * @private
1147
 * @details A string starts with a Windows drive letter if all of the following
1148
 * are true:
1149
 *
1150
 *   - its length is greater than or equal to 2
1151
 *   - its first two code points are a Windows drive letter
1152
 *   - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F
1153
 * (?), or U+0023 (#).
1154
 *
1155
 * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
1156
 */
1157
inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept;
1158
1159
/**
1160
 * @private
1161
 * @details A normalized Windows drive letter is a Windows drive letter of which
1162
 * the second code point is U+003A (:).
1163
 */
1164
inline constexpr bool is_normalized_windows_drive_letter(
1165
    std::string_view input) noexcept;
1166
1167
/**
1168
 * @private
1169
 * Returns true if an input is an ipv4 address. It is assumed that the string
1170
 * does not contain uppercase ASCII characters (the input should have been
1171
 * lowered cased before calling this function) and is not empty.
1172
 */
1173
ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept;
1174
1175
/**
1176
 * @private
1177
 * Cheap pre-check for the WHATWG "ends in a number" / IPv4-host cases.
1178
 * False means the host cannot be IPv4: the last label is empty, does not
1179
 * start with an ASCII digit, or contains a character other than a hex
1180
 * digit or x/X. True means the slower IPv4 parsers should run.
1181
 *
1182
 * Safe on mixed-case input (unlike is_ipv4, which expects a lowercased
1183
 * host).
1184
 */
1185
ada_really_inline constexpr bool last_label_may_be_a_number(
1186
    std::string_view view) noexcept;
1187
1188
/**
1189
 * @private
1190
 * Returns a bitset. If the first bit is set, then at least one character needs
1191
 * percent encoding. If the second bit is set, a \\ is found. If the third bit
1192
 * is set then we have a dot. If the fourth bit is set, then we have a percent
1193
 * character.
1194
 */
1195
ada_really_inline constexpr uint8_t path_signature(
1196
    std::string_view input) noexcept;
1197
1198
/**
1199
 * @private
1200
 * Returns true if the length of the domain name and its labels are according to
1201
 * the specifications. The length of the domain must be 255 octets (253
1202
 * characters not including the last 2 which are the empty label reserved at the
1203
 * end). When the empty label is included (a dot at the end), the domain name
1204
 * can have 254 characters. The length of a label must be at least 1 and at most
1205
 * 63 characters.
1206
 * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034
1207
 * @see https://www.unicode.org/reports/tr46/#ToASCII
1208
 */
1209
ada_really_inline constexpr bool verify_dns_length(
1210
    std::string_view input) noexcept;
1211
1212
/**
1213
 * @private
1214
 * Fast-path parser for pure decimal IPv4 addresses (e.g., "192.168.1.1").
1215
 * Returns the packed 32-bit IPv4 address on success, or a value > 0xFFFFFFFF
1216
 * to indicate failure (caller should fall back to general parser).
1217
 * This is optimized for the common case where the input is a well-formed
1218
 * decimal IPv4 address with exactly 4 octets.
1219
 */
1220
ada_really_inline uint64_t try_parse_ipv4_fast(std::string_view input) noexcept;
1221
1222
/**
1223
 * Sentinel value indicating try_parse_ipv4_fast() did not succeed.
1224
 * Any value > 0xFFFFFFFF indicates the fast path should not be used.
1225
 */
1226
constexpr uint64_t ipv4_fast_fail = uint64_t(1) << 32;
1227
1228
}  // namespace ada::checkers
1229
1230
#endif  // ADA_CHECKERS_H
1231
/* end file include/ada/checkers.h */
1232
1233
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1234
#include <immintrin.h>
1235
#endif
1236
1237
namespace ada::checkers {
1238
1239
102k
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1240
1241
476k
constexpr bool is_ipv4_number_char(char x) noexcept {
1242
476k
  const unsigned char c = static_cast<unsigned char>(x);
1243
476k
  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
1244
259k
         (c >= 'A' && c <= 'F') || c == 'x' || c == 'X';
1245
476k
}
1246
1247
237k
constexpr bool last_label_may_be_a_number(std::string_view view) noexcept {
1248
237k
  if (view.empty()) {
1249
0
    return false;
1250
0
  }
1251
237k
  const char* start = view.data();
1252
237k
  const char* end = start + view.size();
1253
237k
  if (end[-1] == '.') {
1254
8.19k
    --end;
1255
8.19k
    if (end == start) {
1256
3.71k
      return false;
1257
3.71k
    }
1258
8.19k
  }
1259
234k
  if (!is_ipv4_number_char(end[-1])) {
1260
189k
    return false;
1261
189k
  }
1262
44.7k
  const char* label = end;
1263
266k
  while (label != start && is_ipv4_number_char(label[-1])) {
1264
221k
    --label;
1265
221k
  }
1266
44.7k
  if (label != start && label[-1] != '.') {
1267
12.3k
    return false;
1268
12.3k
  }
1269
32.3k
  return label != end && is_digit(*label);
1270
44.7k
}
1271
1272
427k
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1273
1274
241k
constexpr bool is_alpha(char x) noexcept {
1275
241k
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1276
241k
}
1277
1278
62.4k
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1279
62.4k
  return input.size() >= 2 &&
1280
52.1k
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1281
8.70k
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1282
4.69k
                                  input[2] == '?' || input[2] == '#'));
1283
62.4k
}
1284
1285
constexpr bool is_normalized_windows_drive_letter(
1286
16.5k
    std::string_view input) noexcept {
1287
16.5k
  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
1288
16.5k
}
1289
1290
namespace detail {
1291
1292
// Unrolled pure-decimal IPv4. The common portable path for 7-16 byte hosts.
1293
ada_really_inline uint64_t
1294
66.0k
parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
1295
66.0k
  uint32_t ipv4 = 0;
1296
84.8k
  for (int i = 0; i < 4; ++i) {
1297
81.2k
    if (p == pend) [[unlikely]] {
1298
36
      return ipv4_fast_fail;
1299
36
    }
1300
81.2k
    uint32_t val;
1301
81.2k
    char c = *p;
1302
81.2k
    if (c >= '0' && c <= '9') [[likely]] {
1303
27.1k
      val = static_cast<uint32_t>(c - '0');
1304
27.1k
      ++p;
1305
54.0k
    } else {
1306
54.0k
      return ipv4_fast_fail;
1307
54.0k
    }
1308
27.1k
    if (p < pend) {
1309
25.3k
      c = *p;
1310
25.3k
      if (c >= '0' && c <= '9') {
1311
10.1k
        if (val == 0) [[unlikely]] {
1312
1.41k
          return ipv4_fast_fail;
1313
1.41k
        }
1314
8.69k
        val = val * 10u + static_cast<uint32_t>(c - '0');
1315
8.69k
        ++p;
1316
8.69k
        if (p < pend) {
1317
7.81k
          c = *p;
1318
7.81k
          if (c >= '0' && c <= '9') {
1319
4.77k
            val = val * 10u + static_cast<uint32_t>(c - '0');
1320
4.77k
            ++p;
1321
4.77k
            if (val > 255u) [[unlikely]] {
1322
1.33k
              return ipv4_fast_fail;
1323
1.33k
            }
1324
4.77k
          }
1325
7.81k
        }
1326
8.69k
      }
1327
25.3k
    }
1328
24.3k
    ipv4 = (ipv4 << 8) | val;
1329
24.3k
    if (i < 3) {
1330
20.7k
      if (p == pend || *p != '.') [[unlikely]] {
1331
5.54k
        return ipv4_fast_fail;
1332
5.54k
      }
1333
15.2k
      ++p;
1334
15.2k
    }
1335
24.3k
  }
1336
3.63k
  if (p != pend) {
1337
1.63k
    if (p == pend - 1 && *p == '.') {
1338
320
      return ipv4;
1339
320
    }
1340
1.31k
    return ipv4_fast_fail;
1341
1.63k
  }
1342
2.00k
  return ipv4;
1343
3.63k
}
1344
1345
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1346
// Table-free AVX-512VL IPv4 parse (simdip parse_ipv4_avx512vl_notab5).
1347
// Needs VBMI2 for vpcompressb; ADA_AVX512 stays BW+VL (IPv6 does not use
1348
// VBMI2). Masked load of exactly `len` bytes (no over-read). Digit
1349
// placement is computed from compressed delimiter positions; octet > 255
1350
// is a dword compare on the zero-padded reversed digit group, in parallel
1351
// with the convert. Unusual-but-valid forms (octal, hex, leading zeros,
1352
// fewer than four parts) return ipv4_fast_fail so the general parser runs.
1353
ada_really_inline uint64_t try_parse_ipv4_avx512(const char* data,
1354
                                                 size_t len) noexcept {
1355
  // One trailing dot is WHATWG-legal ("1.2.3.4."); the SIMD kernel is
1356
  // strict four-group dotted-decimal.
1357
  if (data[len - 1] == '.') {
1358
    --len;
1359
  }
1360
  if (len > 15) [[unlikely]] {
1361
    return ipv4_fast_fail;
1362
  }
1363
1364
#if defined(__BMI2__)
1365
  const uint32_t len_mask = _bzhi_u32(0xFFFFFFFFu, static_cast<unsigned>(len));
1366
#else
1367
  const uint32_t len_mask = (1u << static_cast<unsigned>(len)) - 1u;
1368
#endif
1369
  const __mmask16 len_k = static_cast<__mmask16>(len_mask);
1370
  const __m128i dot = _mm_set1_epi8('.');
1371
  const __m128i v =
1372
      _mm_mask_loadu_epi8(dot, len_k, reinterpret_cast<const void*>(data));
1373
1374
  const __mmask16 delim = _mm_cmpeq_epi8_mask(v, dot);
1375
  const uint32_t dots = static_cast<uint32_t>(delim) & len_mask;
1376
  const uint32_t keep = len_mask & ~dots;
1377
1378
  const __m128i zero = _mm_set1_epi8('0');
1379
  const __m128i digits = _mm_sub_epi8(v, zero);
1380
  const __mmask16 is_digit = _mm_cmple_epu8_mask(digits, _mm_set1_epi8(9));
1381
  // Junk in a digit slot: inside [0, len) yet neither a digit nor a dot.
1382
  const __mmask16 hole = _kandn_mask16(_kor_mask16(delim, is_digit), len_k);
1383
  const __m128i v0 = _mm_maskz_mov_epi8(is_digit, digits);
1384
1385
  const __m128i iota =
1386
      _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
1387
  const __m128i c = _mm_maskz_compress_epi8(delim, iota);
1388
  const __m128i k_rep =
1389
      _mm_setr_epi8(0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3);
1390
  const __m128i qi = _mm_shuffle_epi8(c, k_rep);
1391
  const __m128i prev =
1392
      _mm_shuffle_epi8(_mm_alignr_epi8(c, _mm_set1_epi8(-1), 15), k_rep);
1393
  // Reversed group order: lane 4i+j fetches q_i-(j+1), so the dword is
1394
  // ones | tens<<8 | hundreds<<16, monotone in the decimal value.
1395
  const __m128i offr = _mm_setr_epi8(-1, -2, -3, -4, -1, -2, -3, -4, -1, -2, -3,
1396
                                     -4, -1, -2, -3, -4);
1397
  const __m128i idx = _mm_max_epi8(_mm_add_epi8(qi, offr), prev);
1398
  const __m128i padded = _mm_shuffle_epi8(v0, idx);
1399
1400
  const __m128i lim =
1401
      _mm_setr_epi8(5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0);
1402
  const __mmask8 over = _mm_cmpgt_epu32_mask(padded, lim);
1403
  const __m128i wtsr =
1404
      _mm_setr_epi8(1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0);
1405
#if defined(__AVX512VNNI__)
1406
  const __m128i res = _mm_dpbusd_epi32(_mm_setzero_si128(), padded, wtsr);
1407
#else
1408
  const __m128i res =
1409
      _mm_madd_epi16(_mm_maddubs_epi16(padded, wtsr), _mm_set1_epi16(1));
1410
#endif
1411
1412
  const __m128i gmin =
1413
      _mm_setr_epi8(1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1414
  const __m128i gmax =
1415
      _mm_setr_epi8(2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
1416
  const __m128i gap = _mm_sub_epi8(c, _mm_slli_si128(c, 1));
1417
  const __mmask16 bad_gap = _mm_cmpgt_epu8_mask(_mm_sub_epi8(gap, gmin), gmax);
1418
1419
  const uint32_t zero_bits =
1420
      static_cast<uint32_t>(_mm_cmpeq_epi8_mask(v, zero));
1421
  const uint32_t start_bits = (dots << 1) | 1u;
1422
1423
  const __mmask16 kerr =
1424
      _kor_mask16(_kor_mask16(hole, static_cast<__mmask16>(over)), bad_gap);
1425
  const uint32_t gerr = static_cast<uint32_t>(_mm_popcnt_u32(dots) ^ 3u) |
1426
                        (zero_bits & start_bits & (keep >> 1));
1427
1428
  if ((gerr | static_cast<uint32_t>(kerr)) == 0) [[likely]] {
1429
    const uint32_t packed =
1430
        static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_cvtepi32_epi8(res)));
1431
#if defined(__GNUC__) || defined(__clang__)
1432
    return static_cast<uint64_t>(__builtin_bswap32(packed));
1433
#else
1434
    return static_cast<uint64_t>(_byteswap_ulong(packed));
1435
#endif
1436
  }
1437
  return ipv4_fast_fail;
1438
}
1439
#endif  // ADA_AVX512 && __AVX512VBMI2__
1440
1441
}  // namespace detail
1442
1443
/**
1444
 * Fast pure-decimal IPv4 parse. Returns packed address or ipv4_fast_fail.
1445
 * Accepts an optional single trailing dot.
1446
 *
1447
 * On AVX-512BW+VL+VBMI2 targets, uses a table-free SIMD parse (masked
1448
 * load, no source over-read) based on parse_ipv4_avx512vl_notab5.
1449
 * Otherwise uses an unrolled scalar path (typically faster than SSE2/NEON
1450
 * pre-validation for these 7-16 byte hosts).
1451
 */
1452
ada_really_inline uint64_t
1453
151k
try_parse_ipv4_fast(std::string_view input) noexcept {
1454
151k
  const size_t len = input.size();
1455
  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
1456
151k
  if (len < 7 || len > 16) [[unlikely]] {
1457
85.5k
    return ipv4_fast_fail;
1458
85.5k
  }
1459
66.0k
  const char* data = input.data();
1460
1461
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1462
  return detail::try_parse_ipv4_avx512(data, len);
1463
#else
1464
66.0k
  return detail::parse_ipv4_decimal_scalar(data, data + len);
1465
151k
#endif
1466
151k
}
1467
1468
}  // namespace ada::checkers
1469
1470
#endif  // ADA_CHECKERS_INL_H
1471
/* end file include/ada/checkers-inl.h */
1472
/* begin file include/ada/log.h */
1473
/**
1474
 * @file log.h
1475
 * @brief Includes the definitions for logging.
1476
 * @private Excluded from docs through the doxygen file.
1477
 */
1478
#ifndef ADA_LOG_H
1479
#define ADA_LOG_H
1480
1481
// To enable logging, set ADA_LOGGING to 1:
1482
#ifndef ADA_LOGGING
1483
#define ADA_LOGGING 0
1484
#endif
1485
1486
#if ADA_LOGGING
1487
#include <iostream>
1488
#endif  // ADA_LOGGING
1489
1490
namespace ada {
1491
1492
/**
1493
 * Log a message. If you want to have no overhead when logging is disabled, use
1494
 * the ada_log macro.
1495
 * @private
1496
 */
1497
template <typename... Args>
1498
constexpr ada_really_inline void log([[maybe_unused]] Args... args) {
1499
#if ADA_LOGGING
1500
  ((std::cout << "ADA_LOG: ") << ... << args) << std::endl;
1501
#endif  // ADA_LOGGING
1502
}
1503
}  // namespace ada
1504
1505
#if ADA_LOGGING
1506
#ifndef ada_log
1507
#define ada_log(...)       \
1508
  do {                     \
1509
    ada::log(__VA_ARGS__); \
1510
  } while (0)
1511
#endif  // ada_log
1512
#else
1513
#define ada_log(...)
1514
#endif  // ADA_LOGGING
1515
1516
#endif  // ADA_LOG_H
1517
/* end file include/ada/log.h */
1518
/* begin file include/ada/encoding_type.h */
1519
/**
1520
 * @file encoding_type.h
1521
 * @brief Character encoding type definitions.
1522
 *
1523
 * Defines the encoding types supported for URL processing.
1524
 *
1525
 * @see https://encoding.spec.whatwg.org/
1526
 */
1527
#ifndef ADA_ENCODING_TYPE_H
1528
#define ADA_ENCODING_TYPE_H
1529
1530
#include <string>
1531
1532
namespace ada {
1533
1534
/**
1535
 * @brief Character encoding types for URL processing.
1536
 *
1537
 * Specifies the character encoding used for percent-decoding and other
1538
 * string operations. UTF-8 is the most commonly used encoding for URLs.
1539
 *
1540
 * @see https://encoding.spec.whatwg.org/#encodings
1541
 */
1542
enum class encoding_type {
1543
  UTF8,     /**< UTF-8 encoding (default for URLs) */
1544
  UTF_16LE, /**< UTF-16 Little Endian encoding */
1545
  UTF_16BE, /**< UTF-16 Big Endian encoding */
1546
};
1547
1548
/**
1549
 * Converts an encoding_type to its string representation.
1550
 * @param type The encoding type to convert.
1551
 * @return A string view of the encoding name.
1552
 */
1553
ada_warn_unused std::string_view to_string(encoding_type type);
1554
1555
}  // namespace ada
1556
1557
#endif  // ADA_ENCODING_TYPE_H
1558
/* end file include/ada/encoding_type.h */
1559
/* begin file include/ada/helpers.h */
1560
/**
1561
 * @file helpers.h
1562
 * @brief Definitions for helper functions used within Ada.
1563
 */
1564
#ifndef ADA_HELPERS_H
1565
#define ADA_HELPERS_H
1566
1567
/* begin file include/ada/url_base.h */
1568
/**
1569
 * @file url_base.h
1570
 * @brief Base class and common definitions for URL types.
1571
 *
1572
 * This file defines the `url_base` abstract base class from which both
1573
 * `ada::url` and `ada::url_aggregator` inherit. It also defines common
1574
 * enumerations like `url_host_type`.
1575
 */
1576
#ifndef ADA_URL_BASE_H
1577
#define ADA_URL_BASE_H
1578
1579
/* begin file include/ada/scheme.h */
1580
/**
1581
 * @file scheme.h
1582
 * @brief URL scheme type definitions and utilities.
1583
 *
1584
 * This header defines the URL scheme types (http, https, etc.) and provides
1585
 * functions to identify special schemes and their default ports according
1586
 * to the WHATWG URL Standard.
1587
 *
1588
 * @see https://url.spec.whatwg.org/#special-scheme
1589
 */
1590
#ifndef ADA_SCHEME_H
1591
#define ADA_SCHEME_H
1592
1593
1594
#include <string>
1595
1596
/**
1597
 * @namespace ada::scheme
1598
 * @brief URL scheme utilities and constants.
1599
 *
1600
 * Provides functions for working with URL schemes, including identification
1601
 * of special schemes and retrieval of default port numbers.
1602
 */
1603
namespace ada::scheme {
1604
1605
/**
1606
 * @brief Enumeration of URL scheme types.
1607
 *
1608
 * Special schemes have specific parsing rules and default ports.
1609
 * Using an enum allows efficient scheme comparisons without string operations.
1610
 *
1611
 * Default ports:
1612
 * - HTTP: 80
1613
 * - HTTPS: 443
1614
 * - WS: 80
1615
 * - WSS: 443
1616
 * - FTP: 21
1617
 * - FILE: (none)
1618
 */
1619
enum type : uint8_t {
1620
  HTTP = 0,        /**< http:// scheme (port 80) */
1621
  NOT_SPECIAL = 1, /**< Non-special scheme (no default port) */
1622
  HTTPS = 2,       /**< https:// scheme (port 443) */
1623
  WS = 3,          /**< ws:// WebSocket scheme (port 80) */
1624
  FTP = 4,         /**< ftp:// scheme (port 21) */
1625
  WSS = 5,         /**< wss:// secure WebSocket scheme (port 443) */
1626
  FILE = 6         /**< file:// scheme (no default port) */
1627
};
1628
1629
/**
1630
 * Checks if a scheme string is a special scheme.
1631
 * @param scheme The scheme string to check (e.g., "http", "https").
1632
 * @return `true` if the scheme is special, `false` otherwise.
1633
 * @see https://url.spec.whatwg.org/#special-scheme
1634
 */
1635
ada_really_inline constexpr bool is_special(std::string_view scheme);
1636
1637
/**
1638
 * Returns the default port for a special scheme string.
1639
 * @param scheme The scheme string (e.g., "http", "https").
1640
 * @return The default port number, or 0 if not a special scheme.
1641
 * @see https://url.spec.whatwg.org/#special-scheme
1642
 */
1643
constexpr uint16_t get_special_port(std::string_view scheme) noexcept;
1644
1645
/**
1646
 * Returns the default port for a scheme type.
1647
 * @param type The scheme type enum value.
1648
 * @return The default port number, or 0 if not applicable.
1649
 * @see https://url.spec.whatwg.org/#special-scheme
1650
 */
1651
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept;
1652
1653
/**
1654
 * Converts a scheme string to its type enum.
1655
 * @param scheme The scheme string to convert.
1656
 * @return The corresponding scheme type, or NOT_SPECIAL if not recognized.
1657
 */
1658
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept;
1659
1660
}  // namespace ada::scheme
1661
1662
#endif  // ADA_SCHEME_H
1663
/* end file include/ada/scheme.h */
1664
1665
#include <string>
1666
#include <string_view>
1667
1668
namespace ada {
1669
1670
/**
1671
 * @brief Enum representing the type of host in a URL.
1672
 *
1673
 * Used to distinguish between regular domain names, IPv4 addresses,
1674
 * and IPv6 addresses for proper parsing and serialization.
1675
 */
1676
enum url_host_type : uint8_t {
1677
  /** Regular domain name (e.g., "www.example.com") */
1678
  DEFAULT = 0,
1679
  /** IPv4 address (e.g., "127.0.0.1") */
1680
  IPV4 = 1,
1681
  /** IPv6 address (e.g., "[::1]" or "[2001:db8::1]") */
1682
  IPV6 = 2,
1683
};
1684
1685
/**
1686
 * @brief Abstract base class for URL representations.
1687
 *
1688
 * The `url_base` class provides the common interface and state shared by
1689
 * both `ada::url` and `ada::url_aggregator`. It contains basic URL attributes
1690
 * like validity status and scheme type, but delegates component storage and
1691
 * access to derived classes.
1692
 *
1693
 * @note This is an abstract class and cannot be instantiated directly.
1694
 *       Use `ada::url` or `ada::url_aggregator` instead.
1695
 *
1696
 * @see url
1697
 * @see url_aggregator
1698
 */
1699
struct url_base {
1700
711k
  virtual ~url_base() = default;
1701
1702
  /**
1703
   * Indicates whether the URL was successfully parsed.
1704
   * Set to `false` if parsing failed (e.g., invalid URL syntax).
1705
   */
1706
  bool is_valid{true};
1707
1708
  /**
1709
   * Indicates whether the URL has an opaque path (non-hierarchical).
1710
   * Opaque paths occur in non-special URLs like `mailto:` or `javascript:`.
1711
   */
1712
  bool has_opaque_path{false};
1713
1714
  /**
1715
   * The type of the URL's host (domain, IPv4, or IPv6).
1716
   */
1717
  url_host_type host_type = url_host_type::DEFAULT;
1718
1719
  /**
1720
   * @private
1721
   * Internal representation of the URL's scheme type.
1722
   */
1723
  ada::scheme::type type{ada::scheme::type::NOT_SPECIAL};
1724
1725
  /**
1726
   * Checks if the URL has a special scheme (http, https, ws, wss, ftp, file).
1727
   * Special schemes have specific parsing rules and default ports.
1728
   * @return `true` if the scheme is special, `false` otherwise.
1729
   */
1730
  [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept;
1731
1732
  /**
1733
   * Returns the URL's origin (scheme + host + port for special URLs).
1734
   * @return A newly allocated string containing the serialized origin.
1735
   * @see https://url.spec.whatwg.org/#concept-url-origin
1736
   */
1737
  [[nodiscard]] virtual std::string get_origin() const = 0;
1738
1739
  /**
1740
   * Validates whether the hostname is a valid domain according to RFC 1034.
1741
   * Checks that the domain and its labels have valid lengths.
1742
   * @return `true` if the domain is valid, `false` otherwise.
1743
   */
1744
  [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0;
1745
1746
  /**
1747
   * @private
1748
   * Returns the default port for special schemes (e.g., 443 for https).
1749
   * Returns 0 for file:// URLs or non-special schemes.
1750
   */
1751
  [[nodiscard]] inline uint16_t get_special_port() const noexcept;
1752
1753
  /**
1754
   * @private
1755
   * Returns the default port for the URL's scheme, or 0 if none.
1756
   */
1757
  [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept;
1758
1759
  /**
1760
   * @private
1761
   * Parses a port number from the input string.
1762
   * @param view The string containing the port to parse.
1763
   * @param check_trailing_content Whether to validate no trailing characters.
1764
   * @return Number of bytes consumed on success, 0 on failure.
1765
   */
1766
  virtual size_t parse_port(std::string_view view,
1767
                            bool check_trailing_content) = 0;
1768
1769
  /** @private */
1770
0
  virtual ada_really_inline size_t parse_port(std::string_view view) {
1771
0
    return this->parse_port(view, false);
1772
0
  }
1773
1774
  /**
1775
   * Returns a JSON string representation of this URL for debugging.
1776
   * @return A JSON-formatted string with URL information.
1777
   */
1778
  [[nodiscard]] virtual std::string to_string() const = 0;
1779
1780
  /** @private */
1781
  virtual inline void clear_pathname() = 0;
1782
1783
  /** @private */
1784
  virtual inline void clear_search() = 0;
1785
1786
  /** @private */
1787
  [[nodiscard]] virtual inline bool has_hash() const noexcept = 0;
1788
1789
  /** @private */
1790
  [[nodiscard]] virtual inline bool has_search() const noexcept = 0;
1791
1792
};  // url_base
1793
1794
}  // namespace ada
1795
1796
#endif
1797
/* end file include/ada/url_base.h */
1798
1799
#include <string>
1800
#include <string_view>
1801
#include <optional>
1802
1803
#if ADA_DEVELOPMENT_CHECKS
1804
#include <iostream>
1805
#endif  // ADA_DEVELOPMENT_CHECKS
1806
1807
/**
1808
 * These functions are not part of our public API and may
1809
 * change at any time.
1810
 *
1811
 * @private
1812
 * @namespace ada::helpers
1813
 * @brief Includes the definitions for helper functions
1814
 */
1815
namespace ada::helpers {
1816
1817
/**
1818
 * @private
1819
 */
1820
template <typename out_iter>
1821
void encode_json(std::string_view view, out_iter out);
1822
1823
/**
1824
 * @private
1825
 * This function is used to prune a fragment from a url, and returning the
1826
 * removed string if input has fragment.
1827
 *
1828
 * @details prune_hash seeks the first '#' and returns everything after it
1829
 * as a string_view, and modifies (in place) the input so that it points at
1830
 * everything before the '#'. If no '#' is found, the input is left unchanged
1831
 * and std::nullopt is returned.
1832
 *
1833
 * @attention The function is non-allocating and it does not throw.
1834
 * @returns Note that the returned string_view might be empty!
1835
 */
1836
ada_really_inline std::optional<std::string_view> prune_hash(
1837
    std::string_view& input) noexcept;
1838
1839
/**
1840
 * @private
1841
 * Defined by the URL specification, shorten a URLs paths.
1842
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1843
 * @returns Returns true if path is shortened.
1844
 */
1845
ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type);
1846
1847
/**
1848
 * @private
1849
 * Defined by the URL specification, shorten a URLs paths.
1850
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1851
 * @returns Returns true if path is shortened.
1852
 */
1853
ada_really_inline bool shorten_path(std::string_view& path,
1854
                                    ada::scheme::type type);
1855
1856
/**
1857
 * @private
1858
 *
1859
 * Parse the path from the provided input and append to the existing
1860
 * (possibly empty) path. The input cannot contain tabs and spaces: it
1861
 * is the user's responsibility to check.
1862
 *
1863
 * The input is expected to be UTF-8.
1864
 *
1865
 * @see https://url.spec.whatwg.org/
1866
 */
1867
ada_really_inline void parse_prepared_path(std::string_view input,
1868
                                           ada::scheme::type type,
1869
                                           std::string& path);
1870
1871
/**
1872
 * @private
1873
 * Remove and mutate all ASCII tab or newline characters from an input.
1874
 */
1875
ada_really_inline void remove_ascii_tab_or_newline(std::string& input);
1876
1877
/**
1878
 * @private
1879
 * Return the substring from input going from index pos to the end.
1880
 */
1881
ada_really_inline constexpr std::string_view substring(std::string_view input,
1882
                                                       size_t pos);
1883
1884
/**
1885
 * @private
1886
 * Returns true if the string_view points within the string.
1887
 */
1888
bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1889
1890
/**
1891
 * @private
1892
 * Return the substring from input going from index pos1 to the pos2 (non
1893
 * included). The length of the substring is pos2 - pos1.
1894
 */
1895
ada_really_inline constexpr std::string_view substring(std::string_view input,
1896
                                                       size_t pos1,
1897
732k
                                                       size_t pos2) {
1898
#if ADA_DEVELOPMENT_CHECKS
1899
  if (pos2 < pos1) {
1900
    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1901
              << std::endl;
1902
    abort();
1903
  }
1904
#endif
1905
732k
  return input.substr(pos1, pos2 - pos1);
1906
732k
}
1907
1908
/**
1909
 * @private
1910
 * Modify the string_view so that it has the new size pos, assuming that pos <=
1911
 * input.size(). This function cannot throw.
1912
 */
1913
ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1914
1915
/**
1916
 * @private
1917
 * Returns a host's delimiter location depending on the state of the instance,
1918
 * and whether a colon was found outside brackets. Used by the host parser.
1919
 */
1920
ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1921
    bool is_special, std::string_view& view) noexcept;
1922
1923
/**
1924
 * @private
1925
 * Removes leading and trailing C0 control and whitespace characters from
1926
 * string.
1927
 */
1928
void trim_c0_whitespace(std::string_view& input) noexcept;
1929
1930
/**
1931
 * @private
1932
 * @see
1933
 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1934
 */
1935
template <class url_type>
1936
ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url);
1937
1938
/**
1939
 * @private
1940
 * Finds the delimiter of a view in authority state.
1941
 */
1942
ada_really_inline size_t
1943
find_authority_delimiter_special(std::string_view view) noexcept;
1944
1945
/**
1946
 * @private
1947
 * Finds the delimiter of a view in authority state.
1948
 */
1949
ada_really_inline size_t
1950
find_authority_delimiter(std::string_view view) noexcept;
1951
1952
/**
1953
 * @private
1954
 */
1955
template <typename T, typename... Args>
1956
259k
inline void inner_concat(std::string& buffer, T t) {
1957
259k
  buffer.append(t);
1958
259k
}
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
1956
54.8k
inline void inner_concat(std::string& buffer, T t) {
1957
54.8k
  buffer.append(t);
1958
54.8k
}
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
1956
158k
inline void inner_concat(std::string& buffer, T t) {
1957
158k
  buffer.append(t);
1958
158k
}
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
1956
46.0k
inline void inner_concat(std::string& buffer, T t) {
1957
46.0k
  buffer.append(t);
1958
46.0k
}
1959
1960
/**
1961
 * @private
1962
 */
1963
template <typename T, typename... Args>
1964
336k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1965
336k
  buffer.append(t);
1966
336k
  return inner_concat(buffer, args...);
1967
336k
}
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
1964
54.8k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1965
54.8k
  buffer.append(t);
1966
54.8k
  return inner_concat(buffer, args...);
1967
54.8k
}
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
1964
30.8k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1965
30.8k
  buffer.append(t);
1966
30.8k
  return inner_concat(buffer, args...);
1967
30.8k
}
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
1964
143k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1965
143k
  buffer.append(t);
1966
143k
  return inner_concat(buffer, args...);
1967
143k
}
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
1964
14.8k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1965
14.8k
  buffer.append(t);
1966
14.8k
  return inner_concat(buffer, args...);
1967
14.8k
}
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
1964
46.0k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1965
46.0k
  buffer.append(t);
1966
46.0k
  return inner_concat(buffer, args...);
1967
46.0k
}
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
1964
46.0k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1965
46.0k
  buffer.append(t);
1966
46.0k
  return inner_concat(buffer, args...);
1967
46.0k
}
1968
1969
/**
1970
 * @private
1971
 * Concatenate the arguments and return a string.
1972
 * @returns a string
1973
 */
1974
template <typename... Args>
1975
259k
std::string concat(Args... args) {
1976
259k
  std::string answer;
1977
259k
  inner_concat(answer, args...);
1978
259k
  return answer;
1979
259k
}
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
1975
23.9k
std::string concat(Args... args) {
1976
23.9k
  std::string answer;
1977
23.9k
  inner_concat(answer, args...);
1978
23.9k
  return answer;
1979
23.9k
}
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
1975
30.8k
std::string concat(Args... args) {
1976
30.8k
  std::string answer;
1977
30.8k
  inner_concat(answer, args...);
1978
30.8k
  return answer;
1979
30.8k
}
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
1975
143k
std::string concat(Args... args) {
1976
143k
  std::string answer;
1977
143k
  inner_concat(answer, args...);
1978
143k
  return answer;
1979
143k
}
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
1975
14.8k
std::string concat(Args... args) {
1976
14.8k
  std::string answer;
1977
14.8k
  inner_concat(answer, args...);
1978
14.8k
  return answer;
1979
14.8k
}
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
1975
27
std::string concat(Args... args) {
1976
27
  std::string answer;
1977
27
  inner_concat(answer, args...);
1978
27
  return answer;
1979
27
}
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
1975
46.0k
std::string concat(Args... args) {
1976
46.0k
  std::string answer;
1977
46.0k
  inner_concat(answer, args...);
1978
46.0k
  return answer;
1979
46.0k
}
1980
1981
/**
1982
 * @private
1983
 * @return Number of leading zeroes.
1984
 */
1985
153k
inline int leading_zeroes(uint32_t input_num) noexcept {
1986
#if ADA_REGULAR_VISUAL_STUDIO
1987
  unsigned long leading_zero(0);
1988
  unsigned long in(input_num);
1989
  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
1990
#else
1991
153k
  return __builtin_clz(input_num);
1992
153k
#endif  // ADA_REGULAR_VISUAL_STUDIO
1993
153k
}
1994
1995
/**
1996
 * @private
1997
 * Counts the number of decimal digits necessary to represent x.
1998
 * faster than std::to_string(x).size().
1999
 * @return digit count
2000
 */
2001
40.4k
inline int fast_digit_count(uint32_t x) noexcept {
2002
40.4k
  auto int_log2 = [](uint32_t z) -> int {
2003
40.4k
    return 31 - ada::helpers::leading_zeroes(z | 1);
2004
40.4k
  };
2005
  // Compiles to very few instructions. Note that the
2006
  // table is static and thus effectively a constant.
2007
  // We leave it inside the function because it is meaningless
2008
  // outside of it (this comes at no performance cost).
2009
40.4k
  const static uint64_t table[] = {
2010
40.4k
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
2011
40.4k
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
2012
40.4k
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
2013
40.4k
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
2014
40.4k
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
2015
40.4k
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
2016
40.4k
      42949672960, 42949672960};
2017
40.4k
  return int((x + table[int_log2(x)]) >> 32);
2018
40.4k
}
2019
}  // namespace ada::helpers
2020
2021
#endif  // ADA_HELPERS_H
2022
/* end file include/ada/helpers.h */
2023
/* begin file include/ada/parser.h */
2024
/**
2025
 * @file parser.h
2026
 * @brief Low-level URL parsing functions.
2027
 *
2028
 * This header provides the internal URL parsing implementation. Most users
2029
 * should use `ada::parse()` from implementation.h instead of these functions
2030
 * directly.
2031
 *
2032
 * @see implementation.h for the recommended public API
2033
 */
2034
#ifndef ADA_PARSER_H
2035
#define ADA_PARSER_H
2036
2037
#include <string_view>
2038
#include <variant>
2039
2040
/* begin file include/ada/expected.h */
2041
/**
2042
 * @file expected.h
2043
 * @brief Definitions for std::expected
2044
 * @private Excluded from docs through the doxygen file.
2045
 */
2046
///
2047
// expected - An implementation of std::expected with extensions
2048
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
2049
//
2050
// Documentation available at http://tl.tartanllama.xyz/
2051
//
2052
// To the extent possible under law, the author(s) have dedicated all
2053
// copyright and related and neighboring rights to this software to the
2054
// public domain worldwide. This software is distributed without any warranty.
2055
//
2056
// You should have received a copy of the CC0 Public Domain Dedication
2057
// along with this software. If not, see
2058
// <http://creativecommons.org/publicdomain/zero/1.0/>.
2059
///
2060
2061
#ifndef TL_EXPECTED_HPP
2062
#define TL_EXPECTED_HPP
2063
2064
#define TL_EXPECTED_VERSION_MAJOR 1
2065
#define TL_EXPECTED_VERSION_MINOR 1
2066
#define TL_EXPECTED_VERSION_PATCH 0
2067
2068
#include <exception>
2069
#include <functional>
2070
#include <type_traits>
2071
#include <utility>
2072
2073
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
2074
#define TL_EXPECTED_EXCEPTIONS_ENABLED
2075
#endif
2076
2077
#if (defined(_MSC_VER) && _MSC_VER == 1900)
2078
#define TL_EXPECTED_MSVC2015
2079
#define TL_EXPECTED_MSVC2015_CONSTEXPR
2080
#else
2081
#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
2082
#endif
2083
2084
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2085
     !defined(__clang__))
2086
#define TL_EXPECTED_GCC49
2087
#endif
2088
2089
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
2090
     !defined(__clang__))
2091
#define TL_EXPECTED_GCC54
2092
#endif
2093
2094
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
2095
     !defined(__clang__))
2096
#define TL_EXPECTED_GCC55
2097
#endif
2098
2099
#if !defined(TL_ASSERT)
2100
// can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
2101
#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
2102
#include <cassert>
2103
2.29M
#define TL_ASSERT(x) assert(x)
2104
#else
2105
#define TL_ASSERT(x)
2106
#endif
2107
#endif
2108
2109
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2110
     !defined(__clang__))
2111
// GCC < 5 doesn't support overloading on const&& for member functions
2112
2113
#define TL_EXPECTED_NO_CONSTRR
2114
// GCC < 5 doesn't support some standard C++11 type traits
2115
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2116
  std::has_trivial_copy_constructor<T>
2117
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2118
  std::has_trivial_copy_assign<T>
2119
2120
// This one will be different for GCC 5.7 if it's ever supported
2121
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2122
  std::is_trivially_destructible<T>
2123
2124
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
2125
// std::vector for non-copyable types
2126
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
2127
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2128
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2129
namespace tl {
2130
namespace detail {
2131
template <class T>
2132
struct is_trivially_copy_constructible
2133
    : std::is_trivially_copy_constructible<T> {};
2134
#ifdef _GLIBCXX_VECTOR
2135
template <class T, class A>
2136
struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
2137
#endif
2138
}  // namespace detail
2139
}  // namespace tl
2140
#endif
2141
2142
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2143
  tl::detail::is_trivially_copy_constructible<T>
2144
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2145
  std::is_trivially_copy_assignable<T>
2146
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2147
  std::is_trivially_destructible<T>
2148
#else
2149
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2150
  std::is_trivially_copy_constructible<T>
2151
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2152
  std::is_trivially_copy_assignable<T>
2153
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2154
  std::is_trivially_destructible<T>
2155
#endif
2156
2157
#if __cplusplus > 201103L
2158
#define TL_EXPECTED_CXX14
2159
#endif
2160
2161
#ifdef TL_EXPECTED_GCC49
2162
#define TL_EXPECTED_GCC49_CONSTEXPR
2163
#else
2164
#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
2165
#endif
2166
2167
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
2168
     defined(TL_EXPECTED_GCC49))
2169
#define TL_EXPECTED_11_CONSTEXPR
2170
#else
2171
#define TL_EXPECTED_11_CONSTEXPR constexpr
2172
#endif
2173
2174
namespace tl {
2175
template <class T, class E>
2176
class expected;
2177
2178
#ifndef TL_MONOSTATE_INPLACE_MUTEX
2179
#define TL_MONOSTATE_INPLACE_MUTEX
2180
class monostate {};
2181
2182
struct in_place_t {
2183
  explicit in_place_t() = default;
2184
};
2185
static constexpr in_place_t in_place{};
2186
#endif
2187
2188
template <class E>
2189
class unexpected {
2190
 public:
2191
  static_assert(!std::is_same<E, void>::value, "E must not be void");
2192
2193
  unexpected() = delete;
2194
  constexpr explicit unexpected(const E& e) : m_val(e) {}
2195
2196
130k
  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
2197
2198
  template <class... Args, typename std::enable_if<std::is_constructible<
2199
                               E, Args&&...>::value>::type* = nullptr>
2200
  constexpr explicit unexpected(Args&&... args)
2201
0
      : m_val(std::forward<Args>(args)...) {}
2202
  template <
2203
      class U, class... Args,
2204
      typename std::enable_if<std::is_constructible<
2205
          E, std::initializer_list<U>&, Args&&...>::value>::type* = nullptr>
2206
  constexpr explicit unexpected(std::initializer_list<U> l, Args&&... args)
2207
      : m_val(l, std::forward<Args>(args)...) {}
2208
2209
  constexpr const E& value() const& { return m_val; }
2210
65.0k
  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
2211
  TL_EXPECTED_11_CONSTEXPR E&& value() && { return std::move(m_val); }
2212
  constexpr const E&& value() const&& { return std::move(m_val); }
2213
2214
 private:
2215
  E m_val;
2216
};
2217
2218
#ifdef __cpp_deduction_guides
2219
template <class E>
2220
unexpected(E) -> unexpected<E>;
2221
#endif
2222
2223
template <class E>
2224
constexpr bool operator==(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2225
  return lhs.value() == rhs.value();
2226
}
2227
template <class E>
2228
constexpr bool operator!=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2229
  return lhs.value() != rhs.value();
2230
}
2231
template <class E>
2232
constexpr bool operator<(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2233
  return lhs.value() < rhs.value();
2234
}
2235
template <class E>
2236
constexpr bool operator<=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2237
  return lhs.value() <= rhs.value();
2238
}
2239
template <class E>
2240
constexpr bool operator>(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2241
  return lhs.value() > rhs.value();
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
2248
template <class E>
2249
unexpected<typename std::decay<E>::type> make_unexpected(E&& e) {
2250
  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
2251
}
2252
2253
struct unexpect_t {
2254
  unexpect_t() = default;
2255
};
2256
static constexpr unexpect_t unexpect{};
2257
2258
namespace detail {
2259
template <typename E>
2260
0
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E&& e) {
2261
0
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2262
0
  throw std::forward<E>(e);
2263
#else
2264
  (void)e;
2265
#ifdef _MSC_VER
2266
  __assume(0);
2267
#else
2268
  __builtin_unreachable();
2269
#endif
2270
#endif
2271
0
}
2272
2273
#ifndef TL_TRAITS_MUTEX
2274
#define TL_TRAITS_MUTEX
2275
// C++14-style aliases for brevity
2276
template <class T>
2277
using remove_const_t = typename std::remove_const<T>::type;
2278
template <class T>
2279
using remove_reference_t = typename std::remove_reference<T>::type;
2280
template <class T>
2281
using decay_t = typename std::decay<T>::type;
2282
template <bool E, class T = void>
2283
using enable_if_t = typename std::enable_if<E, T>::type;
2284
template <bool B, class T, class F>
2285
using conditional_t = typename std::conditional<B, T, F>::type;
2286
2287
// std::conjunction from C++17
2288
template <class...>
2289
struct conjunction : std::true_type {};
2290
template <class B>
2291
struct conjunction<B> : B {};
2292
template <class B, class... Bs>
2293
struct conjunction<B, Bs...>
2294
    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2295
2296
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2297
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2298
#endif
2299
2300
// In C++11 mode, there's an issue in libc++'s std::mem_fn
2301
// which results in a hard-error when using it in a noexcept expression
2302
// in some cases. This is a check to workaround the common failing case.
2303
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2304
template <class T>
2305
struct is_pointer_to_non_const_member_func : std::false_type {};
2306
template <class T, class Ret, class... Args>
2307
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
2308
    : std::true_type {};
2309
template <class T, class Ret, class... Args>
2310
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
2311
    : std::true_type {};
2312
template <class T, class Ret, class... Args>
2313
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
2314
    : std::true_type {};
2315
template <class T, class Ret, class... Args>
2316
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
2317
    : std::true_type {};
2318
template <class T, class Ret, class... Args>
2319
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&>
2320
    : std::true_type {};
2321
template <class T, class Ret, class... Args>
2322
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&>
2323
    : std::true_type {};
2324
2325
template <class T>
2326
struct is_const_or_const_ref : std::false_type {};
2327
template <class T>
2328
struct is_const_or_const_ref<T const&> : std::true_type {};
2329
template <class T>
2330
struct is_const_or_const_ref<T const> : std::true_type {};
2331
#endif
2332
2333
// std::invoke from C++17
2334
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2335
template <
2336
    typename Fn, typename... Args,
2337
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2338
    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2339
                             is_const_or_const_ref<Args...>::value)>,
2340
#endif
2341
    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2342
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2343
    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2344
    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2345
  return std::mem_fn(f)(std::forward<Args>(args)...);
2346
}
2347
2348
template <typename Fn, typename... Args,
2349
          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2350
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2351
    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2352
    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2353
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
2354
}
2355
2356
// std::invoke_result from C++17
2357
template <class F, class, class... Us>
2358
struct invoke_result_impl;
2359
2360
template <class F, class... Us>
2361
struct invoke_result_impl<
2362
    F,
2363
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2364
    Us...> {
2365
  using type =
2366
      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2367
};
2368
2369
template <class F, class... Us>
2370
using invoke_result = invoke_result_impl<F, void, Us...>;
2371
2372
template <class F, class... Us>
2373
using invoke_result_t = typename invoke_result<F, Us...>::type;
2374
2375
#if defined(_MSC_VER) && _MSC_VER <= 1900
2376
// TODO make a version which works with MSVC 2015
2377
template <class T, class U = T>
2378
struct is_swappable : std::true_type {};
2379
2380
template <class T, class U = T>
2381
struct is_nothrow_swappable : std::true_type {};
2382
#else
2383
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2384
namespace swap_adl_tests {
2385
// if swap ADL finds this then it would call std::swap otherwise (same
2386
// signature)
2387
struct tag {};
2388
2389
template <class T>
2390
tag swap(T&, T&);
2391
template <class T, std::size_t N>
2392
tag swap(T (&a)[N], T (&b)[N]);
2393
2394
// helper functions to test if an unqualified swap is possible, and if it
2395
// becomes std::swap
2396
template <class, class>
2397
std::false_type can_swap(...) noexcept(false);
2398
template <class T, class U,
2399
          class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
2400
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(),
2401
                                                    std::declval<U&>())));
2402
2403
template <class, class>
2404
std::false_type uses_std(...);
2405
template <class T, class U>
2406
std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag>
2407
uses_std(int);
2408
2409
template <class T>
2410
struct is_std_swap_noexcept
2411
    : std::integral_constant<bool,
2412
                             std::is_nothrow_move_constructible<T>::value &&
2413
                                 std::is_nothrow_move_assignable<T>::value> {};
2414
2415
template <class T, std::size_t N>
2416
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2417
2418
template <class T, class U>
2419
struct is_adl_swap_noexcept
2420
    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2421
}  // namespace swap_adl_tests
2422
2423
template <class T, class U = T>
2424
struct is_swappable
2425
    : std::integral_constant<
2426
          bool,
2427
          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2428
              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2429
               (std::is_move_assignable<T>::value &&
2430
                std::is_move_constructible<T>::value))> {};
2431
2432
template <class T, std::size_t N>
2433
struct is_swappable<T[N], T[N]>
2434
    : std::integral_constant<
2435
          bool,
2436
          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2437
              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2438
                   0))::value ||
2439
               is_swappable<T, T>::value)> {};
2440
2441
template <class T, class U = T>
2442
struct is_nothrow_swappable
2443
    : std::integral_constant<
2444
          bool,
2445
          is_swappable<T, U>::value &&
2446
              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2447
                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2448
               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2449
                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2450
#endif
2451
#endif
2452
2453
// Trait for checking if a type is a tl::expected
2454
template <class T>
2455
struct is_expected_impl : std::false_type {};
2456
template <class T, class E>
2457
struct is_expected_impl<expected<T, E>> : std::true_type {};
2458
template <class T>
2459
using is_expected = is_expected_impl<decay_t<T>>;
2460
2461
template <class T, class E, class U>
2462
using expected_enable_forward_value = detail::enable_if_t<
2463
    std::is_constructible<T, U&&>::value &&
2464
    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2465
    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2466
    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2467
2468
template <class T, class E, class U, class G, class UR, class GR>
2469
using expected_enable_from_other = detail::enable_if_t<
2470
    std::is_constructible<T, UR>::value &&
2471
    std::is_constructible<E, GR>::value &&
2472
    !std::is_constructible<T, expected<U, G>&>::value &&
2473
    !std::is_constructible<T, expected<U, G>&&>::value &&
2474
    !std::is_constructible<T, const expected<U, G>&>::value &&
2475
    !std::is_constructible<T, const expected<U, G>&&>::value &&
2476
    !std::is_convertible<expected<U, G>&, T>::value &&
2477
    !std::is_convertible<expected<U, G>&&, T>::value &&
2478
    !std::is_convertible<const expected<U, G>&, T>::value &&
2479
    !std::is_convertible<const expected<U, G>&&, T>::value>;
2480
2481
template <class T, class U>
2482
using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2483
2484
template <class T>
2485
using is_copy_constructible_or_void =
2486
    is_void_or<T, std::is_copy_constructible<T>>;
2487
2488
template <class T>
2489
using is_move_constructible_or_void =
2490
    is_void_or<T, std::is_move_constructible<T>>;
2491
2492
template <class T>
2493
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2494
2495
template <class T>
2496
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2497
2498
}  // namespace detail
2499
2500
namespace detail {
2501
struct no_init_t {};
2502
static constexpr no_init_t no_init{};
2503
2504
// Implements the storage of the values, and ensures that the destructor is
2505
// trivial if it can be.
2506
//
2507
// This specialization is for where neither `T` or `E` is trivially
2508
// destructible, so the destructors must be called on destruction of the
2509
// `expected`
2510
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2511
          bool = std::is_trivially_destructible<E>::value>
2512
struct expected_storage_base {
2513
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2514
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2515
2516
  template <class... Args,
2517
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2518
                nullptr>
2519
  constexpr expected_storage_base(in_place_t, Args&&... args)
2520
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2521
2522
  template <class U, class... Args,
2523
            detail::enable_if_t<std::is_constructible<
2524
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2525
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2526
                                  Args&&... args)
2527
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2528
  template <class... Args,
2529
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2530
                nullptr>
2531
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2532
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2533
2534
  template <class U, class... Args,
2535
            detail::enable_if_t<std::is_constructible<
2536
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2537
  constexpr explicit expected_storage_base(unexpect_t,
2538
                                           std::initializer_list<U> il,
2539
                                           Args&&... args)
2540
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2541
2542
  ~expected_storage_base() {
2543
    if (m_has_val) {
2544
      m_val.~T();
2545
    } else {
2546
      m_unexpect.~unexpected<E>();
2547
    }
2548
  }
2549
  union {
2550
    T m_val;
2551
    unexpected<E> m_unexpect;
2552
    char m_no_init;
2553
  };
2554
  bool m_has_val;
2555
};
2556
2557
// This specialization is for when both `T` and `E` are trivially-destructible,
2558
// so the destructor of the `expected` can be trivial.
2559
template <class T, class E>
2560
struct expected_storage_base<T, E, true, true> {
2561
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2562
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2563
2564
  template <class... Args,
2565
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2566
                nullptr>
2567
  constexpr expected_storage_base(in_place_t, Args&&... args)
2568
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_
2569
2570
  template <class U, class... Args,
2571
            detail::enable_if_t<std::is_constructible<
2572
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2573
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2574
                                  Args&&... args)
2575
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2576
  template <class... Args,
2577
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2578
                nullptr>
2579
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2580
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2581
2582
  template <class U, class... Args,
2583
            detail::enable_if_t<std::is_constructible<
2584
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2585
  constexpr explicit expected_storage_base(unexpect_t,
2586
                                           std::initializer_list<U> il,
2587
                                           Args&&... args)
2588
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2589
2590
  ~expected_storage_base() = default;
2591
  union {
2592
    T m_val;
2593
    unexpected<E> m_unexpect;
2594
    char m_no_init;
2595
  };
2596
  bool m_has_val;
2597
};
2598
2599
// T is trivial, E is not.
2600
template <class T, class E>
2601
struct expected_storage_base<T, E, true, false> {
2602
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2603
  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2604
      : m_no_init(), m_has_val(false) {}
2605
2606
  template <class... Args,
2607
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2608
                nullptr>
2609
  constexpr expected_storage_base(in_place_t, Args&&... args)
2610
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2611
2612
  template <class U, class... Args,
2613
            detail::enable_if_t<std::is_constructible<
2614
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2615
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2616
                                  Args&&... args)
2617
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2618
  template <class... Args,
2619
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2620
                nullptr>
2621
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2622
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2623
2624
  template <class U, class... Args,
2625
            detail::enable_if_t<std::is_constructible<
2626
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2627
  constexpr explicit expected_storage_base(unexpect_t,
2628
                                           std::initializer_list<U> il,
2629
                                           Args&&... args)
2630
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2631
2632
  ~expected_storage_base() {
2633
    if (!m_has_val) {
2634
      m_unexpect.~unexpected<E>();
2635
    }
2636
  }
2637
2638
  union {
2639
    T m_val;
2640
    unexpected<E> m_unexpect;
2641
    char m_no_init;
2642
  };
2643
  bool m_has_val;
2644
};
2645
2646
// E is trivial, T is not.
2647
template <class T, class E>
2648
struct expected_storage_base<T, E, false, true> {
2649
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2650
0
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2651
2652
  template <class... Args,
2653
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2654
                nullptr>
2655
  constexpr expected_storage_base(in_place_t, Args&&... args)
2656
251k
      : 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
2656
88.6k
      : 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
2656
162k
      : 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_
2657
2658
  template <class U, class... Args,
2659
            detail::enable_if_t<std::is_constructible<
2660
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2661
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2662
                                  Args&&... args)
2663
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2664
  template <class... Args,
2665
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2666
                nullptr>
2667
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2668
65.0k
      : 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
2668
30.6k
      : 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
2668
34.3k
      : 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_
2669
2670
  template <class U, class... Args,
2671
            detail::enable_if_t<std::is_constructible<
2672
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2673
  constexpr explicit expected_storage_base(unexpect_t,
2674
                                           std::initializer_list<U> il,
2675
                                           Args&&... args)
2676
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2677
2678
316k
  ~expected_storage_base() {
2679
316k
    if (m_has_val) {
2680
251k
      m_val.~T();
2681
251k
    }
2682
316k
  }
tl::detail::expected_storage_base<ada::url, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2678
119k
  ~expected_storage_base() {
2679
119k
    if (m_has_val) {
2680
88.6k
      m_val.~T();
2681
88.6k
    }
2682
119k
  }
tl::detail::expected_storage_base<ada::url_aggregator, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2678
196k
  ~expected_storage_base() {
2679
196k
    if (m_has_val) {
2680
162k
      m_val.~T();
2681
162k
    }
2682
196k
  }
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()
2683
  union {
2684
    T m_val;
2685
    unexpected<E> m_unexpect;
2686
    char m_no_init;
2687
  };
2688
  bool m_has_val;
2689
};
2690
2691
// `T` is `void`, `E` is trivially-destructible
2692
template <class E>
2693
struct expected_storage_base<void, E, false, true> {
2694
#if __GNUC__ <= 5
2695
// no constexpr for GCC 4/5 bug
2696
#else
2697
  TL_EXPECTED_MSVC2015_CONSTEXPR
2698
#endif
2699
  expected_storage_base() : m_has_val(true) {}
2700
2701
  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2702
2703
  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2704
2705
  template <class... Args,
2706
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2707
                nullptr>
2708
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2709
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2710
2711
  template <class U, class... Args,
2712
            detail::enable_if_t<std::is_constructible<
2713
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2714
  constexpr explicit expected_storage_base(unexpect_t,
2715
                                           std::initializer_list<U> il,
2716
                                           Args&&... args)
2717
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2718
2719
  ~expected_storage_base() = default;
2720
  struct dummy {};
2721
  union {
2722
    unexpected<E> m_unexpect;
2723
    dummy m_val;
2724
  };
2725
  bool m_has_val;
2726
};
2727
2728
// `T` is `void`, `E` is not trivially-destructible
2729
template <class E>
2730
struct expected_storage_base<void, E, false, false> {
2731
  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2732
  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2733
2734
  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2735
2736
  template <class... Args,
2737
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2738
                nullptr>
2739
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2740
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2741
2742
  template <class U, class... Args,
2743
            detail::enable_if_t<std::is_constructible<
2744
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2745
  constexpr explicit expected_storage_base(unexpect_t,
2746
                                           std::initializer_list<U> il,
2747
                                           Args&&... args)
2748
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2749
2750
  ~expected_storage_base() {
2751
    if (!m_has_val) {
2752
      m_unexpect.~unexpected<E>();
2753
    }
2754
  }
2755
2756
  union {
2757
    unexpected<E> m_unexpect;
2758
    char m_dummy;
2759
  };
2760
  bool m_has_val;
2761
};
2762
2763
// This base class provides some handy member functions which can be used in
2764
// further derived classes
2765
template <class T, class E>
2766
struct expected_operations_base : expected_storage_base<T, E> {
2767
  using expected_storage_base<T, E>::expected_storage_base;
2768
2769
  template <class... Args>
2770
  void construct(Args&&... args) noexcept {
2771
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2772
    this->m_has_val = true;
2773
  }
2774
2775
  template <class Rhs>
2776
  // NOLINTNEXTLINE(bugprone-exception-escape)
2777
0
  void construct_with(Rhs&& rhs) noexcept {
2778
0
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2779
0
    this->m_has_val = true;
2780
0
  }
2781
2782
  template <class... Args>
2783
0
  void construct_error(Args&&... args) noexcept {
2784
0
    new (std::addressof(this->m_unexpect))
2785
0
        unexpected<E>(std::forward<Args>(args)...);
2786
0
    this->m_has_val = false;
2787
0
  }
2788
2789
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2790
2791
  // These assign overloads ensure that the most efficient assignment
2792
  // implementation is used while maintaining the strong exception guarantee.
2793
  // The problematic case is where rhs has a value, but *this does not.
2794
  //
2795
  // This overload handles the case where we can just copy-construct `T`
2796
  // directly into place without throwing.
2797
  template <class U = T,
2798
            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>* =
2799
                nullptr>
2800
  void assign(const expected_operations_base& rhs) noexcept {
2801
    if (!this->m_has_val && rhs.m_has_val) {
2802
      geterr().~unexpected<E>();
2803
      construct(rhs.get());
2804
    } else {
2805
      assign_common(rhs);
2806
    }
2807
  }
2808
2809
  // This overload handles the case where we can attempt to create a copy of
2810
  // `T`, then no-throw move it into place if the copy was successful.
2811
  template <class U = T,
2812
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2813
                                std::is_nothrow_move_constructible<U>::value>* =
2814
                nullptr>
2815
  void assign(const expected_operations_base& rhs) noexcept {
2816
    if (!this->m_has_val && rhs.m_has_val) {
2817
      T tmp = rhs.get();
2818
      geterr().~unexpected<E>();
2819
      construct(std::move(tmp));
2820
    } else {
2821
      assign_common(rhs);
2822
    }
2823
  }
2824
2825
  // This overload is the worst-case, where we have to move-construct the
2826
  // unexpected value into temporary storage, then try to copy the T into place.
2827
  // If the construction succeeds, then everything is fine, but if it throws,
2828
  // then we move the old unexpected value back into place before rethrowing the
2829
  // exception.
2830
  template <class U = T,
2831
            detail::enable_if_t<
2832
                !std::is_nothrow_copy_constructible<U>::value &&
2833
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2834
  void assign(const expected_operations_base& rhs) {
2835
    if (!this->m_has_val && rhs.m_has_val) {
2836
      auto tmp = std::move(geterr());
2837
      geterr().~unexpected<E>();
2838
2839
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2840
      try {
2841
        construct(rhs.get());
2842
      } catch (...) {
2843
        geterr() = std::move(tmp);
2844
        throw;
2845
      }
2846
#else
2847
      construct(rhs.get());
2848
#endif
2849
    } else {
2850
      assign_common(rhs);
2851
    }
2852
  }
2853
2854
  // These overloads do the same as above, but for rvalues
2855
  template <class U = T,
2856
            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>* =
2857
                nullptr>
2858
  void assign(expected_operations_base&& rhs) noexcept {
2859
    if (!this->m_has_val && rhs.m_has_val) {
2860
      geterr().~unexpected<E>();
2861
      construct(std::move(rhs).get());
2862
    } else {
2863
      assign_common(std::move(rhs));
2864
    }
2865
  }
2866
2867
  template <class U = T,
2868
            detail::enable_if_t<
2869
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2870
  void assign(expected_operations_base&& rhs) {
2871
    if (!this->m_has_val && rhs.m_has_val) {
2872
      auto tmp = std::move(geterr());
2873
      geterr().~unexpected<E>();
2874
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2875
      try {
2876
        construct(std::move(rhs).get());
2877
      } catch (...) {
2878
        geterr() = std::move(tmp);
2879
        throw;
2880
      }
2881
#else
2882
      construct(std::move(rhs).get());
2883
#endif
2884
    } else {
2885
      assign_common(std::move(rhs));
2886
    }
2887
  }
2888
2889
#else
2890
2891
  // If exceptions are disabled then we can just copy-construct
2892
  void assign(const expected_operations_base& rhs) noexcept {
2893
    if (!this->m_has_val && rhs.m_has_val) {
2894
      geterr().~unexpected<E>();
2895
      construct(rhs.get());
2896
    } else {
2897
      assign_common(rhs);
2898
    }
2899
  }
2900
2901
  void assign(expected_operations_base&& rhs) noexcept {
2902
    if (!this->m_has_val && rhs.m_has_val) {
2903
      geterr().~unexpected<E>();
2904
      construct(std::move(rhs).get());
2905
    } else {
2906
      assign_common(std::move(rhs));
2907
    }
2908
  }
2909
2910
#endif
2911
2912
  // The common part of move/copy assigning
2913
  template <class Rhs>
2914
  void assign_common(Rhs&& rhs) {
2915
    if (this->m_has_val) {
2916
      if (rhs.m_has_val) {
2917
        get() = std::forward<Rhs>(rhs).get();
2918
      } else {
2919
        destroy_val();
2920
        construct_error(std::forward<Rhs>(rhs).geterr());
2921
      }
2922
    } else {
2923
      if (!rhs.m_has_val) {
2924
        geterr() = std::forward<Rhs>(rhs).geterr();
2925
      }
2926
    }
2927
  }
2928
2929
0
  bool has_value() const { return this->m_has_val; }
2930
2931
  TL_EXPECTED_11_CONSTEXPR T& get() & { return this->m_val; }
2932
0
  constexpr const T& get() const& { return this->m_val; }
2933
  TL_EXPECTED_11_CONSTEXPR T&& get() && { return std::move(this->m_val); }
2934
#ifndef TL_EXPECTED_NO_CONSTRR
2935
  constexpr const T&& get() const&& { return std::move(this->m_val); }
2936
#endif
2937
2938
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2939
    return this->m_unexpect;
2940
  }
2941
0
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2942
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2943
    return std::move(this->m_unexpect);
2944
  }
2945
#ifndef TL_EXPECTED_NO_CONSTRR
2946
  constexpr const unexpected<E>&& geterr() const&& {
2947
    return std::move(this->m_unexpect);
2948
  }
2949
#endif
2950
2951
  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2952
};
2953
2954
// This base class provides some handy member functions which can be used in
2955
// further derived classes
2956
template <class E>
2957
struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2958
  using expected_storage_base<void, E>::expected_storage_base;
2959
2960
  template <class... Args>
2961
  void construct() noexcept {
2962
    this->m_has_val = true;
2963
  }
2964
2965
  // This function doesn't use its argument, but needs it so that code in
2966
  // levels above this can work independently of whether T is void
2967
  template <class Rhs>
2968
  void construct_with(Rhs&&) noexcept {
2969
    this->m_has_val = true;
2970
  }
2971
2972
  template <class... Args>
2973
  void construct_error(Args&&... args) noexcept {
2974
    new (std::addressof(this->m_unexpect))
2975
        unexpected<E>(std::forward<Args>(args)...);
2976
    this->m_has_val = false;
2977
  }
2978
2979
  template <class Rhs>
2980
  void assign(Rhs&& rhs) noexcept {
2981
    if (!this->m_has_val) {
2982
      if (rhs.m_has_val) {
2983
        geterr().~unexpected<E>();
2984
        construct();
2985
      } else {
2986
        geterr() = std::forward<Rhs>(rhs).geterr();
2987
      }
2988
    } else {
2989
      if (!rhs.m_has_val) {
2990
        construct_error(std::forward<Rhs>(rhs).geterr());
2991
      }
2992
    }
2993
  }
2994
2995
  bool has_value() const { return this->m_has_val; }
2996
2997
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2998
    return this->m_unexpect;
2999
  }
3000
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
3001
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
3002
    return std::move(this->m_unexpect);
3003
  }
3004
#ifndef TL_EXPECTED_NO_CONSTRR
3005
  constexpr const unexpected<E>&& geterr() const&& {
3006
    return std::move(this->m_unexpect);
3007
  }
3008
#endif
3009
3010
  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
3011
    // no-op
3012
  }
3013
};
3014
3015
// This class manages conditionally having a trivial copy constructor
3016
// This specialization is for when T and E are trivially copy constructible
3017
template <class T, class E,
3018
          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(
3019
                                   T)>::value &&
3020
                 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
3021
struct expected_copy_base : expected_operations_base<T, E> {
3022
  using expected_operations_base<T, E>::expected_operations_base;
3023
};
3024
3025
// This specialization is for when T or E are not trivially copy constructible
3026
template <class T, class E>
3027
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
3028
  using expected_operations_base<T, E>::expected_operations_base;
3029
3030
  expected_copy_base() = default;
3031
  expected_copy_base(const expected_copy_base& rhs)
3032
0
      : expected_operations_base<T, E>(no_init) {
3033
0
    if (rhs.has_value()) {
3034
0
      this->construct_with(rhs);
3035
0
    } else {
3036
0
      this->construct_error(rhs.geterr());
3037
0
    }
3038
0
  }
3039
3040
  expected_copy_base(expected_copy_base&& rhs) = default;
3041
  expected_copy_base& operator=(const expected_copy_base& rhs) = default;
3042
  expected_copy_base& operator=(expected_copy_base&& rhs) = default;
3043
};
3044
3045
// This class manages conditionally having a trivial move constructor
3046
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3047
// doesn't implement an analogue to std::is_trivially_move_constructible. We
3048
// have to make do with a non-trivial move constructor even if T is trivially
3049
// move constructible
3050
#ifndef TL_EXPECTED_GCC49
3051
template <class T, class E,
3052
          bool =
3053
              is_void_or<T, std::is_trivially_move_constructible<T>>::value &&
3054
              std::is_trivially_move_constructible<E>::value>
3055
struct expected_move_base : expected_copy_base<T, E> {
3056
  using expected_copy_base<T, E>::expected_copy_base;
3057
};
3058
#else
3059
template <class T, class E, bool = false>
3060
struct expected_move_base;
3061
#endif
3062
template <class T, class E>
3063
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
3064
  using expected_copy_base<T, E>::expected_copy_base;
3065
3066
  expected_move_base() = default;
3067
0
  expected_move_base(const expected_move_base& rhs) = default;
3068
3069
  expected_move_base(expected_move_base&& rhs) noexcept(
3070
      std::is_nothrow_move_constructible<T>::value)
3071
      : expected_copy_base<T, E>(no_init) {
3072
    if (rhs.has_value()) {
3073
      this->construct_with(std::move(rhs));
3074
    } else {
3075
      this->construct_error(std::move(rhs.geterr()));
3076
    }
3077
  }
3078
  expected_move_base& operator=(const expected_move_base& rhs) = default;
3079
  expected_move_base& operator=(expected_move_base&& rhs) = default;
3080
};
3081
3082
// This class manages conditionally having a trivial copy assignment operator
3083
template <
3084
    class T, class E,
3085
    bool =
3086
        is_void_or<
3087
            T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
3088
                           TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
3089
                           TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value &&
3090
        TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value &&
3091
        TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value &&
3092
        TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
3093
struct expected_copy_assign_base : expected_move_base<T, E> {
3094
  using expected_move_base<T, E>::expected_move_base;
3095
};
3096
3097
template <class T, class E>
3098
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
3099
  using expected_move_base<T, E>::expected_move_base;
3100
3101
  expected_copy_assign_base() = default;
3102
0
  expected_copy_assign_base(const expected_copy_assign_base& rhs) = default;
3103
3104
  expected_copy_assign_base(expected_copy_assign_base&& rhs) = default;
3105
  expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
3106
    this->assign(rhs);
3107
    return *this;
3108
  }
3109
  expected_copy_assign_base& operator=(expected_copy_assign_base&& rhs) =
3110
      default;
3111
};
3112
3113
// This class manages conditionally having a trivial move assignment operator
3114
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3115
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
3116
// to make do with a non-trivial move assignment operator even if T is trivially
3117
// move assignable
3118
#ifndef TL_EXPECTED_GCC49
3119
template <
3120
    class T, class E,
3121
    bool = is_void_or<
3122
               T, conjunction<std::is_trivially_destructible<T>,
3123
                              std::is_trivially_move_constructible<T>,
3124
                              std::is_trivially_move_assignable<T>>>::value &&
3125
           std::is_trivially_destructible<E>::value &&
3126
           std::is_trivially_move_constructible<E>::value &&
3127
           std::is_trivially_move_assignable<E>::value>
3128
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
3129
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3130
};
3131
#else
3132
template <class T, class E, bool = false>
3133
struct expected_move_assign_base;
3134
#endif
3135
3136
template <class T, class E>
3137
struct expected_move_assign_base<T, E, false>
3138
    : expected_copy_assign_base<T, E> {
3139
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3140
3141
  expected_move_assign_base() = default;
3142
0
  expected_move_assign_base(const expected_move_assign_base& rhs) = default;
3143
3144
  expected_move_assign_base(expected_move_assign_base&& rhs) = default;
3145
3146
  expected_move_assign_base& operator=(const expected_move_assign_base& rhs) =
3147
      default;
3148
3149
  expected_move_assign_base&
3150
  operator=(expected_move_assign_base&& rhs) noexcept(
3151
      std::is_nothrow_move_constructible<T>::value &&
3152
      std::is_nothrow_move_assignable<T>::value) {
3153
    this->assign(std::move(rhs));
3154
    return *this;
3155
  }
3156
};
3157
3158
// expected_delete_ctor_base will conditionally delete copy and move
3159
// constructors depending on whether T is copy/move constructible
3160
template <class T, class E,
3161
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3162
                             std::is_copy_constructible<E>::value),
3163
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3164
                             std::is_move_constructible<E>::value)>
3165
struct expected_delete_ctor_base {
3166
  expected_delete_ctor_base() = default;
3167
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3168
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3169
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3170
      default;
3171
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3172
      default;
3173
};
3174
3175
template <class T, class E>
3176
struct expected_delete_ctor_base<T, E, true, false> {
3177
  expected_delete_ctor_base() = default;
3178
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3179
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3180
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3181
      default;
3182
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3183
      default;
3184
};
3185
3186
template <class T, class E>
3187
struct expected_delete_ctor_base<T, E, false, true> {
3188
  expected_delete_ctor_base() = default;
3189
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3190
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3191
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3192
      default;
3193
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3194
      default;
3195
};
3196
3197
template <class T, class E>
3198
struct expected_delete_ctor_base<T, E, false, false> {
3199
  expected_delete_ctor_base() = default;
3200
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3201
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3202
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3203
      default;
3204
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3205
      default;
3206
};
3207
3208
// expected_delete_assign_base will conditionally delete copy and move
3209
// constructors depending on whether T and E are copy/move constructible +
3210
// assignable
3211
template <class T, class E,
3212
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3213
                             std::is_copy_constructible<E>::value &&
3214
                             is_copy_assignable_or_void<T>::value &&
3215
                             std::is_copy_assignable<E>::value),
3216
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3217
                             std::is_move_constructible<E>::value &&
3218
                             is_move_assignable_or_void<T>::value &&
3219
                             std::is_move_assignable<E>::value)>
3220
struct expected_delete_assign_base {
3221
  expected_delete_assign_base() = default;
3222
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3223
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3224
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3225
      default;
3226
  expected_delete_assign_base& operator=(
3227
      expected_delete_assign_base&&) noexcept = default;
3228
};
3229
3230
template <class T, class E>
3231
struct expected_delete_assign_base<T, E, true, false> {
3232
  expected_delete_assign_base() = default;
3233
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3234
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3235
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3236
      default;
3237
  expected_delete_assign_base& operator=(
3238
      expected_delete_assign_base&&) noexcept = delete;
3239
};
3240
3241
template <class T, class E>
3242
struct expected_delete_assign_base<T, E, false, true> {
3243
  expected_delete_assign_base() = default;
3244
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3245
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3246
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3247
      delete;
3248
  expected_delete_assign_base& operator=(
3249
      expected_delete_assign_base&&) noexcept = default;
3250
};
3251
3252
template <class T, class E>
3253
struct expected_delete_assign_base<T, E, false, false> {
3254
  expected_delete_assign_base() = default;
3255
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3256
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3257
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3258
      delete;
3259
  expected_delete_assign_base& operator=(
3260
      expected_delete_assign_base&&) noexcept = delete;
3261
};
3262
3263
// This is needed to be able to construct the expected_default_ctor_base which
3264
// follows, while still conditionally deleting the default constructor.
3265
struct default_constructor_tag {
3266
  explicit constexpr default_constructor_tag() = default;
3267
};
3268
3269
// expected_default_ctor_base will ensure that expected has a deleted default
3270
// consturctor if T is not default constructible.
3271
// This specialization is for when T is default constructible
3272
template <class T, class E,
3273
          bool Enable =
3274
              std::is_default_constructible<T>::value || std::is_void<T>::value>
3275
struct expected_default_ctor_base {
3276
  constexpr expected_default_ctor_base() noexcept = default;
3277
  constexpr expected_default_ctor_base(
3278
      expected_default_ctor_base const&) noexcept = default;
3279
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3280
      default;
3281
  expected_default_ctor_base& operator=(
3282
      expected_default_ctor_base const&) noexcept = default;
3283
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3284
      default;
3285
3286
316k
  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
3286
119k
  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
3286
196k
  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)
3287
};
3288
3289
// This specialization is for when T is not default constructible
3290
template <class T, class E>
3291
struct expected_default_ctor_base<T, E, false> {
3292
  constexpr expected_default_ctor_base() noexcept = delete;
3293
  constexpr expected_default_ctor_base(
3294
      expected_default_ctor_base const&) noexcept = default;
3295
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3296
      default;
3297
  expected_default_ctor_base& operator=(
3298
      expected_default_ctor_base const&) noexcept = default;
3299
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3300
      default;
3301
3302
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3303
};
3304
}  // namespace detail
3305
3306
template <class E>
3307
class bad_expected_access : public std::exception {
3308
 public:
3309
0
  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3310
3311
0
  virtual const char* what() const noexcept override {
3312
0
    return "Bad expected access";
3313
0
  }
3314
3315
  const E& error() const& { return m_val; }
3316
  E& error() & { return m_val; }
3317
  const E&& error() const&& { return std::move(m_val); }
3318
  E&& error() && { return std::move(m_val); }
3319
3320
 private:
3321
  E m_val;
3322
};
3323
3324
/// An `expected<T, E>` object is an object that contains the storage for
3325
/// another object and manages the lifetime of this contained object `T`.
3326
/// Alternatively it could contain the storage for another unexpected object
3327
/// `E`. The contained object may not be initialized after the expected object
3328
/// has been initialized, and may not be destroyed before the expected object
3329
/// has been destroyed. The initialization state of the contained object is
3330
/// tracked by the expected object.
3331
template <class T, class E>
3332
class expected : private detail::expected_move_assign_base<T, E>,
3333
                 private detail::expected_delete_ctor_base<T, E>,
3334
                 private detail::expected_delete_assign_base<T, E>,
3335
                 private detail::expected_default_ctor_base<T, E> {
3336
  static_assert(!std::is_reference<T>::value, "T must not be a reference");
3337
  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3338
                "T must not be in_place_t");
3339
  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3340
                "T must not be unexpect_t");
3341
  static_assert(
3342
      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3343
      "T must not be unexpected<E>");
3344
  static_assert(!std::is_reference<E>::value, "E must not be a reference");
3345
3346
2.11M
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<ada::url, ada::errors>::valptr()
Line
Count
Source
3346
848k
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<ada::url_aggregator, ada::errors>::valptr()
Line
Count
Source
3346
1.26M
  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()
3347
  const T* valptr() const { return std::addressof(this->m_val); }
3348
  unexpected<E>* errptr() { return std::addressof(this->m_unexpect); }
3349
  const unexpected<E>* errptr() const {
3350
    return std::addressof(this->m_unexpect);
3351
  }
3352
3353
  template <class U = T,
3354
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3355
184k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3356
184k
    return this->m_val;
3357
184k
  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3355
68.3k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3356
68.3k
    return this->m_val;
3357
68.3k
  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3355
116k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3356
116k
    return this->m_val;
3357
116k
  }
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
3358
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()
3359
3360
  template <class U = T,
3361
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3362
  constexpr const U& val() const {
3363
    return this->m_val;
3364
  }
3365
  constexpr const unexpected<E>& err() const { return this->m_unexpect; }
3366
3367
  using impl_base = detail::expected_move_assign_base<T, E>;
3368
  using ctor_base = detail::expected_default_ctor_base<T, E>;
3369
3370
 public:
3371
  typedef T value_type;
3372
  typedef E error_type;
3373
  typedef unexpected<E> unexpected_type;
3374
3375
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3376
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3377
  template <class F>
3378
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & {
3379
    return and_then_impl(*this, std::forward<F>(f));
3380
  }
3381
  template <class F>
3382
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && {
3383
    return and_then_impl(std::move(*this), std::forward<F>(f));
3384
  }
3385
  template <class F>
3386
  constexpr auto and_then(F&& f) const& {
3387
    return and_then_impl(*this, std::forward<F>(f));
3388
  }
3389
3390
#ifndef TL_EXPECTED_NO_CONSTRR
3391
  template <class F>
3392
  constexpr auto and_then(F&& f) const&& {
3393
    return and_then_impl(std::move(*this), std::forward<F>(f));
3394
  }
3395
#endif
3396
3397
#else
3398
  template <class F>
3399
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & -> decltype(and_then_impl(
3400
      std::declval<expected&>(), std::forward<F>(f))) {
3401
    return and_then_impl(*this, std::forward<F>(f));
3402
  }
3403
  template <class F>
3404
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && -> decltype(and_then_impl(
3405
      std::declval<expected&&>(), std::forward<F>(f))) {
3406
    return and_then_impl(std::move(*this), std::forward<F>(f));
3407
  }
3408
  template <class F>
3409
  constexpr auto and_then(F&& f) const& -> decltype(and_then_impl(
3410
      std::declval<expected const&>(), std::forward<F>(f))) {
3411
    return and_then_impl(*this, std::forward<F>(f));
3412
  }
3413
3414
#ifndef TL_EXPECTED_NO_CONSTRR
3415
  template <class F>
3416
  constexpr auto and_then(F&& f) const&& -> decltype(and_then_impl(
3417
      std::declval<expected const&&>(), std::forward<F>(f))) {
3418
    return and_then_impl(std::move(*this), std::forward<F>(f));
3419
  }
3420
#endif
3421
#endif
3422
3423
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3424
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3425
  template <class F>
3426
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) & {
3427
    return expected_map_impl(*this, std::forward<F>(f));
3428
  }
3429
  template <class F>
3430
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) && {
3431
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3432
  }
3433
  template <class F>
3434
  constexpr auto map(F&& f) const& {
3435
    return expected_map_impl(*this, std::forward<F>(f));
3436
  }
3437
  template <class F>
3438
  constexpr auto map(F&& f) const&& {
3439
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3440
  }
3441
#else
3442
  template <class F>
3443
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3444
      std::declval<expected&>(), std::declval<F&&>())) map(F&& f) & {
3445
    return expected_map_impl(*this, std::forward<F>(f));
3446
  }
3447
  template <class F>
3448
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3449
                                                      std::declval<F&&>()))
3450
  map(F&& f) && {
3451
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3452
  }
3453
  template <class F>
3454
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3455
                                       std::declval<F&&>()))
3456
  map(F&& f) const& {
3457
    return expected_map_impl(*this, std::forward<F>(f));
3458
  }
3459
3460
#ifndef TL_EXPECTED_NO_CONSTRR
3461
  template <class F>
3462
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3463
                                       std::declval<F&&>())) map(F&& f)
3464
      const&& {
3465
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3466
  }
3467
#endif
3468
#endif
3469
3470
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3471
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3472
  template <class F>
3473
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) & {
3474
    return expected_map_impl(*this, std::forward<F>(f));
3475
  }
3476
  template <class F>
3477
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) && {
3478
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3479
  }
3480
  template <class F>
3481
  constexpr auto transform(F&& f) const& {
3482
    return expected_map_impl(*this, std::forward<F>(f));
3483
  }
3484
  template <class F>
3485
  constexpr auto transform(F&& f) const&& {
3486
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3487
  }
3488
#else
3489
  template <class F>
3490
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3491
      std::declval<expected&>(), std::declval<F&&>())) transform(F&& f) & {
3492
    return expected_map_impl(*this, std::forward<F>(f));
3493
  }
3494
  template <class F>
3495
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3496
                                                      std::declval<F&&>()))
3497
  transform(F&& f) && {
3498
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3499
  }
3500
  template <class F>
3501
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3502
                                       std::declval<F&&>()))
3503
  transform(F&& f) const& {
3504
    return expected_map_impl(*this, std::forward<F>(f));
3505
  }
3506
3507
#ifndef TL_EXPECTED_NO_CONSTRR
3508
  template <class F>
3509
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3510
                                       std::declval<F&&>())) transform(F&& f)
3511
      const&& {
3512
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3513
  }
3514
#endif
3515
#endif
3516
3517
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3518
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3519
  template <class F>
3520
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) & {
3521
    return map_error_impl(*this, std::forward<F>(f));
3522
  }
3523
  template <class F>
3524
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) && {
3525
    return map_error_impl(std::move(*this), std::forward<F>(f));
3526
  }
3527
  template <class F>
3528
  constexpr auto map_error(F&& f) const& {
3529
    return map_error_impl(*this, std::forward<F>(f));
3530
  }
3531
  template <class F>
3532
  constexpr auto map_error(F&& f) const&& {
3533
    return map_error_impl(std::move(*this), std::forward<F>(f));
3534
  }
3535
#else
3536
  template <class F>
3537
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3538
      std::declval<expected&>(), std::declval<F&&>())) map_error(F&& f) & {
3539
    return map_error_impl(*this, std::forward<F>(f));
3540
  }
3541
  template <class F>
3542
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3543
                                                   std::declval<F&&>()))
3544
  map_error(F&& f) && {
3545
    return map_error_impl(std::move(*this), std::forward<F>(f));
3546
  }
3547
  template <class F>
3548
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3549
                                    std::declval<F&&>()))
3550
  map_error(F&& f) const& {
3551
    return map_error_impl(*this, std::forward<F>(f));
3552
  }
3553
3554
#ifndef TL_EXPECTED_NO_CONSTRR
3555
  template <class F>
3556
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3557
                                    std::declval<F&&>())) map_error(F&& f)
3558
      const&& {
3559
    return map_error_impl(std::move(*this), std::forward<F>(f));
3560
  }
3561
#endif
3562
#endif
3563
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3564
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3565
  template <class F>
3566
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) & {
3567
    return map_error_impl(*this, std::forward<F>(f));
3568
  }
3569
  template <class F>
3570
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) && {
3571
    return map_error_impl(std::move(*this), std::forward<F>(f));
3572
  }
3573
  template <class F>
3574
  constexpr auto transform_error(F&& f) const& {
3575
    return map_error_impl(*this, std::forward<F>(f));
3576
  }
3577
  template <class F>
3578
  constexpr auto transform_error(F&& f) const&& {
3579
    return map_error_impl(std::move(*this), std::forward<F>(f));
3580
  }
3581
#else
3582
  template <class F>
3583
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3584
      std::declval<expected&>(),
3585
      std::declval<F&&>())) transform_error(F&& f) & {
3586
    return map_error_impl(*this, std::forward<F>(f));
3587
  }
3588
  template <class F>
3589
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3590
                                                   std::declval<F&&>()))
3591
  transform_error(F&& f) && {
3592
    return map_error_impl(std::move(*this), std::forward<F>(f));
3593
  }
3594
  template <class F>
3595
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3596
                                    std::declval<F&&>()))
3597
  transform_error(F&& f) const& {
3598
    return map_error_impl(*this, std::forward<F>(f));
3599
  }
3600
3601
#ifndef TL_EXPECTED_NO_CONSTRR
3602
  template <class F>
3603
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3604
                                    std::declval<F&&>())) transform_error(F&& f)
3605
      const&& {
3606
    return map_error_impl(std::move(*this), std::forward<F>(f));
3607
  }
3608
#endif
3609
#endif
3610
  template <class F>
3611
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) & {
3612
    return or_else_impl(*this, std::forward<F>(f));
3613
  }
3614
3615
  template <class F>
3616
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) && {
3617
    return or_else_impl(std::move(*this), std::forward<F>(f));
3618
  }
3619
3620
  template <class F>
3621
  expected constexpr or_else(F&& f) const& {
3622
    return or_else_impl(*this, std::forward<F>(f));
3623
  }
3624
3625
#ifndef TL_EXPECTED_NO_CONSTRR
3626
  template <class F>
3627
  expected constexpr or_else(F&& f) const&& {
3628
    return or_else_impl(std::move(*this), std::forward<F>(f));
3629
  }
3630
#endif
3631
  constexpr expected() = default;
3632
0
  constexpr expected(const expected& rhs) = default;
3633
  constexpr expected(expected&& rhs) = default;
3634
  expected& operator=(const expected& rhs) = default;
3635
  expected& operator=(expected&& rhs) = default;
3636
3637
  template <class... Args,
3638
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
3639
                nullptr>
3640
  constexpr expected(in_place_t, Args&&... args)
3641
251k
      : impl_base(in_place, std::forward<Args>(args)...),
3642
251k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3641
88.6k
      : impl_base(in_place, std::forward<Args>(args)...),
3642
88.6k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3641
162k
      : impl_base(in_place, std::forward<Args>(args)...),
3642
162k
        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_
3643
3644
  template <class U, class... Args,
3645
            detail::enable_if_t<std::is_constructible<
3646
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3647
  constexpr expected(in_place_t, std::initializer_list<U> il, Args&&... args)
3648
      : impl_base(in_place, il, std::forward<Args>(args)...),
3649
        ctor_base(detail::default_constructor_tag{}) {}
3650
3651
  template <
3652
      class G = E,
3653
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3654
      detail::enable_if_t<!std::is_convertible<const G&, E>::value>* = nullptr>
3655
  explicit constexpr expected(const unexpected<G>& e)
3656
      : impl_base(unexpect, e.value()),
3657
        ctor_base(detail::default_constructor_tag{}) {}
3658
3659
  template <
3660
      class G = E,
3661
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3662
      detail::enable_if_t<std::is_convertible<const G&, E>::value>* = nullptr>
3663
  constexpr expected(unexpected<G> const& e)
3664
      : impl_base(unexpect, e.value()),
3665
        ctor_base(detail::default_constructor_tag{}) {}
3666
3667
  template <
3668
      class G = E,
3669
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3670
      detail::enable_if_t<!std::is_convertible<G&&, E>::value>* = nullptr>
3671
  explicit constexpr expected(unexpected<G>&& e) noexcept(
3672
      std::is_nothrow_constructible<E, G&&>::value)
3673
      : impl_base(unexpect, std::move(e.value())),
3674
        ctor_base(detail::default_constructor_tag{}) {}
3675
3676
  template <
3677
      class G = E,
3678
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3679
      detail::enable_if_t<std::is_convertible<G&&, E>::value>* = nullptr>
3680
  constexpr expected(unexpected<G>&& e) noexcept(
3681
      std::is_nothrow_constructible<E, G&&>::value)
3682
65.0k
      : impl_base(unexpect, std::move(e.value())),
3683
65.0k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3682
30.6k
      : impl_base(unexpect, std::move(e.value())),
3683
30.6k
        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
3682
34.3k
      : impl_base(unexpect, std::move(e.value())),
3683
34.3k
        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
3684
3685
  template <class... Args,
3686
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
3687
                nullptr>
3688
  constexpr explicit expected(unexpect_t, Args&&... args)
3689
      : impl_base(unexpect, std::forward<Args>(args)...),
3690
        ctor_base(detail::default_constructor_tag{}) {}
3691
3692
  template <class U, class... Args,
3693
            detail::enable_if_t<std::is_constructible<
3694
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3695
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3696
                              Args&&... args)
3697
      : impl_base(unexpect, il, std::forward<Args>(args)...),
3698
        ctor_base(detail::default_constructor_tag{}) {}
3699
3700
  template <class U, class G,
3701
            detail::enable_if_t<!(std::is_convertible<U const&, T>::value &&
3702
                                  std::is_convertible<G const&, E>::value)>* =
3703
                nullptr,
3704
            detail::expected_enable_from_other<T, E, U, G, const U&,
3705
                                               const G&>* = nullptr>
3706
  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3707
      : ctor_base(detail::default_constructor_tag{}) {
3708
    if (rhs.has_value()) {
3709
      this->construct(*rhs);
3710
    } else {
3711
      this->construct_error(rhs.error());
3712
    }
3713
  }
3714
3715
  template <
3716
      class U, class G,
3717
      detail::enable_if_t<(std::is_convertible<U const&, T>::value &&
3718
                           std::is_convertible<G const&, E>::value)>* = nullptr,
3719
      detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* =
3720
          nullptr>
3721
  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3722
      : ctor_base(detail::default_constructor_tag{}) {
3723
    if (rhs.has_value()) {
3724
      this->construct(*rhs);
3725
    } else {
3726
      this->construct_error(rhs.error());
3727
    }
3728
  }
3729
3730
  template <
3731
      class U, class G,
3732
      detail::enable_if_t<!(std::is_convertible<U&&, T>::value &&
3733
                            std::is_convertible<G&&, E>::value)>* = nullptr,
3734
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3735
  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3736
      : ctor_base(detail::default_constructor_tag{}) {
3737
    if (rhs.has_value()) {
3738
      this->construct(std::move(*rhs));
3739
    } else {
3740
      this->construct_error(std::move(rhs.error()));
3741
    }
3742
  }
3743
3744
  template <
3745
      class U, class G,
3746
      detail::enable_if_t<(std::is_convertible<U&&, T>::value &&
3747
                           std::is_convertible<G&&, E>::value)>* = nullptr,
3748
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3749
  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3750
      : ctor_base(detail::default_constructor_tag{}) {
3751
    if (rhs.has_value()) {
3752
      this->construct(std::move(*rhs));
3753
    } else {
3754
      this->construct_error(std::move(rhs.error()));
3755
    }
3756
  }
3757
3758
  template <class U = T,
3759
            detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr,
3760
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3761
  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3762
      : expected(in_place, std::forward<U>(v)) {}
3763
3764
  template <class U = T,
3765
            detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr,
3766
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3767
  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3768
251k
      : 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
3768
88.6k
      : 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
3768
162k
      : 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_
3769
3770
  template <
3771
      class U = T, class G = T,
3772
      detail::enable_if_t<std::is_nothrow_constructible<T, U&&>::value>* =
3773
          nullptr,
3774
      detail::enable_if_t<!std::is_void<G>::value>* = nullptr,
3775
      detail::enable_if_t<
3776
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3777
           !detail::conjunction<std::is_scalar<T>,
3778
                                std::is_same<T, detail::decay_t<U>>>::value &&
3779
           std::is_constructible<T, U>::value &&
3780
           std::is_assignable<G&, U>::value &&
3781
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3782
  expected& operator=(U&& v) {
3783
    if (has_value()) {
3784
      val() = std::forward<U>(v);
3785
    } else {
3786
      err().~unexpected<E>();
3787
      ::new (valptr()) T(std::forward<U>(v));
3788
      this->m_has_val = true;
3789
    }
3790
3791
    return *this;
3792
  }
3793
3794
  template <
3795
      class U = T, class G = T,
3796
      detail::enable_if_t<!std::is_nothrow_constructible<T, U&&>::value>* =
3797
          nullptr,
3798
      detail::enable_if_t<!std::is_void<U>::value>* = nullptr,
3799
      detail::enable_if_t<
3800
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3801
           !detail::conjunction<std::is_scalar<T>,
3802
                                std::is_same<T, detail::decay_t<U>>>::value &&
3803
           std::is_constructible<T, U>::value &&
3804
           std::is_assignable<G&, U>::value &&
3805
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3806
  expected& operator=(U&& v) {
3807
    if (has_value()) {
3808
      val() = std::forward<U>(v);
3809
    } else {
3810
      auto tmp = std::move(err());
3811
      err().~unexpected<E>();
3812
3813
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3814
      try {
3815
        ::new (valptr()) T(std::forward<U>(v));
3816
        this->m_has_val = true;
3817
      } catch (...) {
3818
        err() = std::move(tmp);
3819
        throw;
3820
      }
3821
#else
3822
      ::new (valptr()) T(std::forward<U>(v));
3823
      this->m_has_val = true;
3824
#endif
3825
    }
3826
3827
    return *this;
3828
  }
3829
3830
  template <class G = E,
3831
            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3832
                                std::is_assignable<G&, G>::value>* = nullptr>
3833
  expected& operator=(const unexpected<G>& rhs) {
3834
    if (!has_value()) {
3835
      err() = rhs;
3836
    } else {
3837
      this->destroy_val();
3838
      ::new (errptr()) unexpected<E>(rhs);
3839
      this->m_has_val = false;
3840
    }
3841
3842
    return *this;
3843
  }
3844
3845
  template <class G = E,
3846
            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3847
                                std::is_move_assignable<G>::value>* = nullptr>
3848
  expected& operator=(unexpected<G>&& rhs) noexcept {
3849
    if (!has_value()) {
3850
      err() = std::move(rhs);
3851
    } else {
3852
      this->destroy_val();
3853
      ::new (errptr()) unexpected<E>(std::move(rhs));
3854
      this->m_has_val = false;
3855
    }
3856
3857
    return *this;
3858
  }
3859
3860
  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3861
                               T, Args&&...>::value>* = nullptr>
3862
  void emplace(Args&&... args) {
3863
    if (has_value()) {
3864
      val().~T();
3865
    } else {
3866
      err().~unexpected<E>();
3867
      this->m_has_val = true;
3868
    }
3869
    ::new (valptr()) T(std::forward<Args>(args)...);
3870
  }
3871
3872
  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3873
                               T, Args&&...>::value>* = nullptr>
3874
  void emplace(Args&&... args) {
3875
    if (has_value()) {
3876
      val().~T();
3877
      ::new (valptr()) T(std::forward<Args>(args)...);
3878
    } else {
3879
      auto tmp = std::move(err());
3880
      err().~unexpected<E>();
3881
3882
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3883
      try {
3884
        ::new (valptr()) T(std::forward<Args>(args)...);
3885
        this->m_has_val = true;
3886
      } catch (...) {
3887
        err() = std::move(tmp);
3888
        throw;
3889
      }
3890
#else
3891
      ::new (valptr()) T(std::forward<Args>(args)...);
3892
      this->m_has_val = true;
3893
#endif
3894
    }
3895
  }
3896
3897
  template <class U, class... Args,
3898
            detail::enable_if_t<std::is_nothrow_constructible<
3899
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3900
  void emplace(std::initializer_list<U> il, Args&&... args) {
3901
    if (has_value()) {
3902
      T t(il, std::forward<Args>(args)...);
3903
      val() = std::move(t);
3904
    } else {
3905
      err().~unexpected<E>();
3906
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3907
      this->m_has_val = true;
3908
    }
3909
  }
3910
3911
  template <class U, class... Args,
3912
            detail::enable_if_t<!std::is_nothrow_constructible<
3913
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3914
  void emplace(std::initializer_list<U> il, Args&&... args) {
3915
    if (has_value()) {
3916
      T t(il, std::forward<Args>(args)...);
3917
      val() = std::move(t);
3918
    } else {
3919
      auto tmp = std::move(err());
3920
      err().~unexpected<E>();
3921
3922
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3923
      try {
3924
        ::new (valptr()) T(il, std::forward<Args>(args)...);
3925
        this->m_has_val = true;
3926
      } catch (...) {
3927
        err() = std::move(tmp);
3928
        throw;
3929
      }
3930
#else
3931
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3932
      this->m_has_val = true;
3933
#endif
3934
    }
3935
  }
3936
3937
 private:
3938
  using t_is_void = std::true_type;
3939
  using t_is_not_void = std::false_type;
3940
  using t_is_nothrow_move_constructible = std::true_type;
3941
  using move_constructing_t_can_throw = std::false_type;
3942
  using e_is_nothrow_move_constructible = std::true_type;
3943
  using move_constructing_e_can_throw = std::false_type;
3944
3945
  void swap_where_both_have_value(expected& /*rhs*/, t_is_void) noexcept {
3946
    // swapping void is a no-op
3947
  }
3948
3949
  void swap_where_both_have_value(expected& rhs, t_is_not_void) {
3950
    using std::swap;
3951
    swap(val(), rhs.val());
3952
  }
3953
3954
  void swap_where_only_one_has_value(expected& rhs, t_is_void) noexcept(
3955
      std::is_nothrow_move_constructible<E>::value) {
3956
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3957
    rhs.err().~unexpected_type();
3958
    std::swap(this->m_has_val, rhs.m_has_val);
3959
  }
3960
3961
  void swap_where_only_one_has_value(expected& rhs, t_is_not_void) {
3962
    swap_where_only_one_has_value_and_t_is_not_void(
3963
        rhs, typename std::is_nothrow_move_constructible<T>::type{},
3964
        typename std::is_nothrow_move_constructible<E>::type{});
3965
  }
3966
3967
  void swap_where_only_one_has_value_and_t_is_not_void(
3968
      expected& rhs, t_is_nothrow_move_constructible,
3969
      e_is_nothrow_move_constructible) noexcept {
3970
    auto temp = std::move(val());
3971
    val().~T();
3972
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3973
    rhs.err().~unexpected_type();
3974
    ::new (rhs.valptr()) T(std::move(temp));
3975
    std::swap(this->m_has_val, rhs.m_has_val);
3976
  }
3977
3978
  void swap_where_only_one_has_value_and_t_is_not_void(
3979
      expected& rhs, t_is_nothrow_move_constructible,
3980
      move_constructing_e_can_throw) {
3981
    auto temp = std::move(val());
3982
    val().~T();
3983
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3984
    try {
3985
      ::new (errptr()) unexpected_type(std::move(rhs.err()));
3986
      rhs.err().~unexpected_type();
3987
      ::new (rhs.valptr()) T(std::move(temp));
3988
      std::swap(this->m_has_val, rhs.m_has_val);
3989
    } catch (...) {
3990
      val() = std::move(temp);
3991
      throw;
3992
    }
3993
#else
3994
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3995
    rhs.err().~unexpected_type();
3996
    ::new (rhs.valptr()) T(std::move(temp));
3997
    std::swap(this->m_has_val, rhs.m_has_val);
3998
#endif
3999
  }
4000
4001
  void swap_where_only_one_has_value_and_t_is_not_void(
4002
      expected& rhs, move_constructing_t_can_throw,
4003
      e_is_nothrow_move_constructible) {
4004
    auto temp = std::move(rhs.err());
4005
    rhs.err().~unexpected_type();
4006
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
4007
    try {
4008
      ::new (rhs.valptr()) T(std::move(val()));
4009
      val().~T();
4010
      ::new (errptr()) unexpected_type(std::move(temp));
4011
      std::swap(this->m_has_val, rhs.m_has_val);
4012
    } catch (...) {
4013
      rhs.err() = std::move(temp);
4014
      throw;
4015
    }
4016
#else
4017
    ::new (rhs.valptr()) T(std::move(val()));
4018
    val().~T();
4019
    ::new (errptr()) unexpected_type(std::move(temp));
4020
    std::swap(this->m_has_val, rhs.m_has_val);
4021
#endif
4022
  }
4023
4024
 public:
4025
  template <class OT = T, class OE = E>
4026
  detail::enable_if_t<detail::is_swappable<OT>::value &&
4027
                      detail::is_swappable<OE>::value &&
4028
                      (std::is_nothrow_move_constructible<OT>::value ||
4029
                       std::is_nothrow_move_constructible<OE>::value)>
4030
  swap(expected& rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
4031
                               detail::is_nothrow_swappable<T>::value &&
4032
                               std::is_nothrow_move_constructible<E>::value &&
4033
                               detail::is_nothrow_swappable<E>::value) {
4034
    if (has_value() && rhs.has_value()) {
4035
      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
4036
    } else if (!has_value() && rhs.has_value()) {
4037
      rhs.swap(*this);
4038
    } else if (has_value()) {
4039
      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
4040
    } else {
4041
      using std::swap;
4042
      swap(err(), rhs.err());
4043
    }
4044
  }
4045
4046
  constexpr const T* operator->() const {
4047
    TL_ASSERT(has_value());
4048
    return valptr();
4049
  }
4050
2.11M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4051
2.11M
    TL_ASSERT(has_value());
4052
2.11M
    return valptr();
4053
2.11M
  }
tl::expected<ada::url, ada::errors>::operator->()
Line
Count
Source
4050
848k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4051
848k
    TL_ASSERT(has_value());
4052
848k
    return valptr();
4053
848k
  }
tl::expected<ada::url_aggregator, ada::errors>::operator->()
Line
Count
Source
4050
1.26M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4051
1.26M
    TL_ASSERT(has_value());
4052
1.26M
    return valptr();
4053
1.26M
  }
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->()
4054
4055
  template <class U = T,
4056
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4057
  constexpr const U& operator*() const& {
4058
    TL_ASSERT(has_value());
4059
    return val();
4060
  }
4061
  template <class U = T,
4062
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4063
184k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4064
184k
    TL_ASSERT(has_value());
4065
184k
    return val();
4066
184k
  }
_ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
4063
68.3k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4064
68.3k
    TL_ASSERT(has_value());
4065
68.3k
    return val();
4066
68.3k
  }
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
4063
116k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4064
116k
    TL_ASSERT(has_value());
4065
116k
    return val();
4066
116k
  }
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
4067
  template <class U = T,
4068
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4069
  constexpr const U&& operator*() const&& {
4070
    TL_ASSERT(has_value());
4071
    return std::move(val());
4072
  }
4073
  template <class U = T,
4074
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4075
  TL_EXPECTED_11_CONSTEXPR U&& operator*() && {
4076
    TL_ASSERT(has_value());
4077
    return std::move(val());
4078
  }
4079
4080
2.39M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url, ada::errors>::has_value() const
Line
Count
Source
4080
964k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url_aggregator, ada::errors>::has_value() const
Line
Count
Source
4080
1.42M
  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
4081
395k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url, ada::errors>::operator bool() const
Line
Count
Source
4081
155k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url_aggregator, ada::errors>::operator bool() const
Line
Count
Source
4081
239k
  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
4082
4083
  template <class U = T,
4084
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4085
  TL_EXPECTED_11_CONSTEXPR const U& value() const& {
4086
    if (!has_value())
4087
      detail::throw_exception(bad_expected_access<E>(err().value()));
4088
    return val();
4089
  }
4090
  template <class U = T,
4091
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4092
0
  TL_EXPECTED_11_CONSTEXPR U& value() & {
4093
0
    if (!has_value())
4094
0
      detail::throw_exception(bad_expected_access<E>(err().value()));
4095
0
    return val();
4096
0
  }
4097
  template <class U = T,
4098
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4099
  TL_EXPECTED_11_CONSTEXPR const U&& value() const&& {
4100
    if (!has_value())
4101
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4102
    return std::move(val());
4103
  }
4104
  template <class U = T,
4105
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4106
  TL_EXPECTED_11_CONSTEXPR U&& value() && {
4107
    if (!has_value())
4108
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4109
    return std::move(val());
4110
  }
4111
4112
  constexpr const E& error() const& {
4113
    TL_ASSERT(!has_value());
4114
    return err().value();
4115
  }
4116
0
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4117
0
    TL_ASSERT(!has_value());
4118
0
    return err().value();
4119
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() &
4120
  constexpr const E&& error() const&& {
4121
    TL_ASSERT(!has_value());
4122
    return std::move(err().value());
4123
  }
4124
  TL_EXPECTED_11_CONSTEXPR E&& error() && {
4125
    TL_ASSERT(!has_value());
4126
    return std::move(err().value());
4127
  }
4128
4129
  template <class U>
4130
  constexpr T value_or(U&& v) const& {
4131
    static_assert(std::is_copy_constructible<T>::value &&
4132
                      std::is_convertible<U&&, T>::value,
4133
                  "T must be copy-constructible and convertible to from U&&");
4134
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
4135
  }
4136
  template <class U>
4137
  TL_EXPECTED_11_CONSTEXPR T value_or(U&& v) && {
4138
    static_assert(std::is_move_constructible<T>::value &&
4139
                      std::is_convertible<U&&, T>::value,
4140
                  "T must be move-constructible and convertible to from U&&");
4141
    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
4142
  }
4143
};
4144
4145
namespace detail {
4146
template <class Exp>
4147
using exp_t = typename detail::decay_t<Exp>::value_type;
4148
template <class Exp>
4149
using err_t = typename detail::decay_t<Exp>::error_type;
4150
template <class Exp, class Ret>
4151
using ret_t = expected<Ret, err_t<Exp>>;
4152
4153
#ifdef TL_EXPECTED_CXX14
4154
template <class Exp, class F,
4155
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4156
          class Ret = decltype(detail::invoke(std::declval<F>(),
4157
                                              *std::declval<Exp>()))>
4158
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4159
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4160
4161
  return exp.has_value()
4162
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4163
             : Ret(unexpect, std::forward<Exp>(exp).error());
4164
}
4165
4166
template <class Exp, class F,
4167
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4168
          class Ret = decltype(detail::invoke(std::declval<F>()))>
4169
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4170
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4171
4172
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4173
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4174
}
4175
#else
4176
template <class>
4177
struct TC;
4178
template <class Exp, class F,
4179
          class Ret = decltype(detail::invoke(std::declval<F>(),
4180
                                              *std::declval<Exp>())),
4181
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr>
4182
auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4183
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4184
4185
  return exp.has_value()
4186
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4187
             : Ret(unexpect, std::forward<Exp>(exp).error());
4188
}
4189
4190
template <class Exp, class F,
4191
          class Ret = decltype(detail::invoke(std::declval<F>())),
4192
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr>
4193
constexpr auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4194
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4195
4196
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4197
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4198
}
4199
#endif
4200
4201
#ifdef TL_EXPECTED_CXX14
4202
template <class Exp, class F,
4203
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4204
          class Ret = decltype(detail::invoke(std::declval<F>(),
4205
                                              *std::declval<Exp>())),
4206
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4207
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4208
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4209
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4210
                                                 *std::forward<Exp>(exp)))
4211
                         : result(unexpect, std::forward<Exp>(exp).error());
4212
}
4213
4214
template <class Exp, class F,
4215
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4216
          class Ret = decltype(detail::invoke(std::declval<F>(),
4217
                                              *std::declval<Exp>())),
4218
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4219
auto expected_map_impl(Exp&& exp, F&& f) {
4220
  using result = expected<void, err_t<Exp>>;
4221
  if (exp.has_value()) {
4222
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4223
    return result();
4224
  }
4225
4226
  return result(unexpect, std::forward<Exp>(exp).error());
4227
}
4228
4229
template <class Exp, class F,
4230
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4231
          class Ret = decltype(detail::invoke(std::declval<F>())),
4232
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4233
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4234
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4235
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4236
                         : result(unexpect, std::forward<Exp>(exp).error());
4237
}
4238
4239
template <class Exp, class F,
4240
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4241
          class Ret = decltype(detail::invoke(std::declval<F>())),
4242
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4243
auto expected_map_impl(Exp&& exp, F&& f) {
4244
  using result = expected<void, err_t<Exp>>;
4245
  if (exp.has_value()) {
4246
    detail::invoke(std::forward<F>(f));
4247
    return result();
4248
  }
4249
4250
  return result(unexpect, std::forward<Exp>(exp).error());
4251
}
4252
#else
4253
template <class Exp, class F,
4254
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4255
          class Ret = decltype(detail::invoke(std::declval<F>(),
4256
                                              *std::declval<Exp>())),
4257
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4258
4259
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4260
    -> ret_t<Exp, detail::decay_t<Ret>> {
4261
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4262
4263
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4264
                                                 *std::forward<Exp>(exp)))
4265
                         : result(unexpect, std::forward<Exp>(exp).error());
4266
}
4267
4268
template <class Exp, class F,
4269
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4270
          class Ret = decltype(detail::invoke(std::declval<F>(),
4271
                                              *std::declval<Exp>())),
4272
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4273
4274
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4275
  if (exp.has_value()) {
4276
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4277
    return {};
4278
  }
4279
4280
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4281
}
4282
4283
template <class Exp, class F,
4284
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4285
          class Ret = decltype(detail::invoke(std::declval<F>())),
4286
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4287
4288
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4289
    -> ret_t<Exp, detail::decay_t<Ret>> {
4290
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4291
4292
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4293
                         : result(unexpect, std::forward<Exp>(exp).error());
4294
}
4295
4296
template <class Exp, class F,
4297
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4298
          class Ret = decltype(detail::invoke(std::declval<F>())),
4299
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4300
4301
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4302
  if (exp.has_value()) {
4303
    detail::invoke(std::forward<F>(f));
4304
    return {};
4305
  }
4306
4307
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4308
}
4309
#endif
4310
4311
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4312
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4313
template <class Exp, class F,
4314
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4315
          class Ret = decltype(detail::invoke(std::declval<F>(),
4316
                                              std::declval<Exp>().error())),
4317
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4318
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4319
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4320
  return exp.has_value()
4321
             ? result(*std::forward<Exp>(exp))
4322
             : result(unexpect, detail::invoke(std::forward<F>(f),
4323
                                               std::forward<Exp>(exp).error()));
4324
}
4325
template <class Exp, class F,
4326
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4327
          class Ret = decltype(detail::invoke(std::declval<F>(),
4328
                                              std::declval<Exp>().error())),
4329
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4330
auto map_error_impl(Exp&& exp, F&& f) {
4331
  using result = expected<exp_t<Exp>, monostate>;
4332
  if (exp.has_value()) {
4333
    return result(*std::forward<Exp>(exp));
4334
  }
4335
4336
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4337
  return result(unexpect, monostate{});
4338
}
4339
template <class Exp, class F,
4340
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4341
          class Ret = decltype(detail::invoke(std::declval<F>(),
4342
                                              std::declval<Exp>().error())),
4343
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4344
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4345
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4346
  return exp.has_value()
4347
             ? result()
4348
             : result(unexpect, detail::invoke(std::forward<F>(f),
4349
                                               std::forward<Exp>(exp).error()));
4350
}
4351
template <class Exp, class F,
4352
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4353
          class Ret = decltype(detail::invoke(std::declval<F>(),
4354
                                              std::declval<Exp>().error())),
4355
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4356
auto map_error_impl(Exp&& exp, F&& f) {
4357
  using result = expected<exp_t<Exp>, monostate>;
4358
  if (exp.has_value()) {
4359
    return result();
4360
  }
4361
4362
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4363
  return result(unexpect, monostate{});
4364
}
4365
#else
4366
template <class Exp, class F,
4367
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4368
          class Ret = decltype(detail::invoke(std::declval<F>(),
4369
                                              std::declval<Exp>().error())),
4370
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4371
constexpr auto map_error_impl(Exp&& exp, F&& f)
4372
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4373
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4374
4375
  return exp.has_value()
4376
             ? result(*std::forward<Exp>(exp))
4377
             : result(unexpect, detail::invoke(std::forward<F>(f),
4378
                                               std::forward<Exp>(exp).error()));
4379
}
4380
4381
template <class Exp, class F,
4382
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4383
          class Ret = decltype(detail::invoke(std::declval<F>(),
4384
                                              std::declval<Exp>().error())),
4385
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4386
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4387
  using result = expected<exp_t<Exp>, monostate>;
4388
  if (exp.has_value()) {
4389
    return result(*std::forward<Exp>(exp));
4390
  }
4391
4392
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4393
  return result(unexpect, monostate{});
4394
}
4395
4396
template <class Exp, class F,
4397
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4398
          class Ret = decltype(detail::invoke(std::declval<F>(),
4399
                                              std::declval<Exp>().error())),
4400
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4401
constexpr auto map_error_impl(Exp&& exp, F&& f)
4402
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4403
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4404
4405
  return exp.has_value()
4406
             ? result()
4407
             : result(unexpect, detail::invoke(std::forward<F>(f),
4408
                                               std::forward<Exp>(exp).error()));
4409
}
4410
4411
template <class Exp, class F,
4412
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4413
          class Ret = decltype(detail::invoke(std::declval<F>(),
4414
                                              std::declval<Exp>().error())),
4415
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4416
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4417
  using result = expected<exp_t<Exp>, monostate>;
4418
  if (exp.has_value()) {
4419
    return result();
4420
  }
4421
4422
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4423
  return result(unexpect, monostate{});
4424
}
4425
#endif
4426
4427
#ifdef TL_EXPECTED_CXX14
4428
template <class Exp, class F,
4429
          class Ret = decltype(detail::invoke(std::declval<F>(),
4430
                                              std::declval<Exp>().error())),
4431
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4432
constexpr auto or_else_impl(Exp&& exp, F&& f) {
4433
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4434
  return exp.has_value() ? std::forward<Exp>(exp)
4435
                         : detail::invoke(std::forward<F>(f),
4436
                                          std::forward<Exp>(exp).error());
4437
}
4438
4439
template <class Exp, class F,
4440
          class Ret = decltype(detail::invoke(std::declval<F>(),
4441
                                              std::declval<Exp>().error())),
4442
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4443
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4444
  return exp.has_value() ? std::forward<Exp>(exp)
4445
                         : (detail::invoke(std::forward<F>(f),
4446
                                           std::forward<Exp>(exp).error()),
4447
                            std::forward<Exp>(exp));
4448
}
4449
#else
4450
template <class Exp, class F,
4451
          class Ret = decltype(detail::invoke(std::declval<F>(),
4452
                                              std::declval<Exp>().error())),
4453
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4454
auto or_else_impl(Exp&& exp, F&& f) -> Ret {
4455
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4456
  return exp.has_value() ? std::forward<Exp>(exp)
4457
                         : detail::invoke(std::forward<F>(f),
4458
                                          std::forward<Exp>(exp).error());
4459
}
4460
4461
template <class Exp, class F,
4462
          class Ret = decltype(detail::invoke(std::declval<F>(),
4463
                                              std::declval<Exp>().error())),
4464
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4465
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4466
  return exp.has_value() ? std::forward<Exp>(exp)
4467
                         : (detail::invoke(std::forward<F>(f),
4468
                                           std::forward<Exp>(exp).error()),
4469
                            std::forward<Exp>(exp));
4470
}
4471
#endif
4472
}  // namespace detail
4473
4474
template <class T, class E, class U, class F>
4475
constexpr bool operator==(const expected<T, E>& lhs,
4476
                          const expected<U, F>& rhs) {
4477
  return (lhs.has_value() != rhs.has_value())
4478
             ? false
4479
             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
4480
}
4481
template <class T, class E, class U, class F>
4482
constexpr bool operator!=(const expected<T, E>& lhs,
4483
                          const expected<U, F>& rhs) {
4484
  return (lhs.has_value() != rhs.has_value())
4485
             ? true
4486
             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4487
}
4488
template <class E, class F>
4489
constexpr bool operator==(const expected<void, E>& lhs,
4490
                          const expected<void, F>& rhs) {
4491
  return (lhs.has_value() != rhs.has_value())
4492
             ? false
4493
             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
4494
}
4495
template <class E, class F>
4496
constexpr bool operator!=(const expected<void, E>& lhs,
4497
                          const expected<void, F>& rhs) {
4498
  return (lhs.has_value() != rhs.has_value())
4499
             ? true
4500
             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4501
}
4502
4503
template <class T, class E, class U>
4504
constexpr bool operator==(const expected<T, E>& x, const U& v) {
4505
  return x.has_value() ? *x == v : false;
4506
}
4507
template <class T, class E, class U>
4508
constexpr bool operator==(const U& v, const expected<T, E>& x) {
4509
  return x.has_value() ? *x == v : false;
4510
}
4511
template <class T, class E, class U>
4512
constexpr bool operator!=(const expected<T, E>& x, const U& v) {
4513
  return x.has_value() ? *x != v : true;
4514
}
4515
template <class T, class E, class U>
4516
constexpr bool operator!=(const U& v, const expected<T, E>& x) {
4517
  return x.has_value() ? *x != v : true;
4518
}
4519
4520
template <class T, class E>
4521
constexpr bool operator==(const expected<T, E>& x, const unexpected<E>& e) {
4522
  return x.has_value() ? false : x.error() == e.value();
4523
}
4524
template <class T, class E>
4525
constexpr bool operator==(const unexpected<E>& e, const expected<T, E>& x) {
4526
  return x.has_value() ? false : x.error() == e.value();
4527
}
4528
template <class T, class E>
4529
constexpr bool operator!=(const expected<T, E>& x, const unexpected<E>& e) {
4530
  return x.has_value() ? true : x.error() != e.value();
4531
}
4532
template <class T, class E>
4533
constexpr bool operator!=(const unexpected<E>& e, const expected<T, E>& x) {
4534
  return x.has_value() ? true : x.error() != e.value();
4535
}
4536
4537
template <class T, class E,
4538
          detail::enable_if_t<(std::is_void<T>::value ||
4539
                               std::is_move_constructible<T>::value) &&
4540
                              detail::is_swappable<T>::value &&
4541
                              std::is_move_constructible<E>::value &&
4542
                              detail::is_swappable<E>::value>* = nullptr>
4543
void swap(expected<T, E>& lhs,
4544
          expected<T, E>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
4545
  lhs.swap(rhs);
4546
}
4547
}  // namespace tl
4548
4549
#endif
4550
/* end file include/ada/expected.h */
4551
4552
/* begin file include/ada/url_pattern_regex.h */
4553
/**
4554
 * @file url_search_params.h
4555
 * @brief Declaration for the URL Search Params
4556
 */
4557
#ifndef ADA_URL_PATTERN_REGEX_H
4558
#define ADA_URL_PATTERN_REGEX_H
4559
4560
#include <string>
4561
#include <string_view>
4562
4563
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4564
#include <regex>
4565
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4566
4567
#if ADA_INCLUDE_URL_PATTERN
4568
namespace ada::url_pattern_regex {
4569
4570
template <typename T>
4571
concept regex_concept = requires(T t, std::string_view pattern,
4572
                                 bool ignore_case, std::string_view input) {
4573
  // Ensure the class has a type alias 'regex_type'
4574
  typename T::regex_type;
4575
4576
  // Function to create a regex instance
4577
  {
4578
    T::create_instance(pattern, ignore_case)
4579
  } -> std::same_as<std::optional<typename T::regex_type>>;
4580
4581
  // Function to perform regex search
4582
  {
4583
    T::regex_search(input, std::declval<typename T::regex_type&>())
4584
  } -> std::same_as<std::optional<std::vector<std::optional<std::string>>>>;
4585
4586
  // Function to match regex pattern
4587
  {
4588
    T::regex_match(input, std::declval<typename T::regex_type&>())
4589
  } -> std::same_as<bool>;
4590
4591
  // Copy constructor
4592
  { T(std::declval<const T&>()) } -> std::same_as<T>;
4593
4594
  // Move constructor
4595
  { T(std::declval<T&&>()) } -> std::same_as<T>;
4596
};
4597
4598
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4599
class std_regex_provider final {
4600
 public:
4601
  std_regex_provider() = default;
4602
  using regex_type = std::regex;
4603
  static std::optional<regex_type> create_instance(std::string_view pattern,
4604
                                                   bool ignore_case);
4605
  static std::optional<std::vector<std::optional<std::string>>> regex_search(
4606
      std::string_view input, const regex_type& pattern);
4607
  static bool regex_match(std::string_view input, const regex_type& pattern);
4608
};
4609
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4610
4611
}  // namespace ada::url_pattern_regex
4612
#endif  // ADA_INCLUDE_URL_PATTERN
4613
#endif  // ADA_URL_PATTERN_REGEX_H
4614
/* end file include/ada/url_pattern_regex.h */
4615
/* begin file include/ada/url_pattern_init.h */
4616
/**
4617
 * @file url_pattern_init.h
4618
 * @brief Declaration for the url_pattern_init implementation.
4619
 */
4620
#ifndef ADA_URL_PATTERN_INIT_H
4621
#define ADA_URL_PATTERN_INIT_H
4622
4623
/* begin file include/ada/errors.h */
4624
/**
4625
 * @file errors.h
4626
 * @brief Error type definitions for URL parsing.
4627
 *
4628
 * Defines the error codes that can be returned when URL parsing fails.
4629
 */
4630
#ifndef ADA_ERRORS_H
4631
#define ADA_ERRORS_H
4632
4633
#include <cstdint>
4634
namespace ada {
4635
/**
4636
 * @brief Error codes for URL parsing operations.
4637
 *
4638
 * Used with `tl::expected` to indicate why a URL parsing operation failed.
4639
 */
4640
enum class errors : uint8_t {
4641
  type_error /**< A type error occurred (e.g., invalid URL syntax). */
4642
};
4643
}  // namespace ada
4644
#endif  // ADA_ERRORS_H
4645
/* end file include/ada/errors.h */
4646
4647
#include <string_view>
4648
#include <string>
4649
#include <optional>
4650
#include <iostream>
4651
4652
#if ADA_TESTING
4653
#include <iostream>
4654
#endif  // ADA_TESTING
4655
4656
#if ADA_INCLUDE_URL_PATTERN
4657
namespace ada {
4658
4659
// Important: C++20 allows us to use concept rather than `using` or `typedef
4660
// and allows functions with second argument, which is optional (using either
4661
// std::nullopt or a parameter with default value)
4662
template <typename F>
4663
concept url_pattern_encoding_callback = requires(F f, std::string_view sv) {
4664
  { f(sv) } -> std::same_as<tl::expected<std::string, errors>>;
4665
};
4666
4667
// A structure providing matching patterns for individual components
4668
// of a URL. When a URLPattern is created, or when a URLPattern is
4669
// used to match or test against a URL, the input can be given as
4670
// either a string or a URLPatternInit struct. If a string is given,
4671
// it will be parsed to create a URLPatternInit. The URLPatternInit
4672
// API is defined as part of the URLPattern specification.
4673
// All provided strings must be valid UTF-8.
4674
struct url_pattern_init {
4675
  enum class process_type : uint8_t {
4676
    url,
4677
    pattern,
4678
  };
4679
4680
0
  friend std::ostream& operator<<(std::ostream& os, process_type type) {
4681
0
    switch (type) {
4682
0
      case process_type::url:
4683
0
        return os << "url";
4684
0
      case process_type::pattern:
4685
0
        return os << "pattern";
4686
0
      default:
4687
0
        return os << "unknown";
4688
0
    }
4689
0
  }
4690
4691
  // All strings must be valid UTF-8.
4692
  // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
4693
  static tl::expected<url_pattern_init, errors> process(
4694
      const url_pattern_init& init, process_type type,
4695
      std::optional<std::string_view> protocol = std::nullopt,
4696
      std::optional<std::string_view> username = std::nullopt,
4697
      std::optional<std::string_view> password = std::nullopt,
4698
      std::optional<std::string_view> hostname = std::nullopt,
4699
      std::optional<std::string_view> port = std::nullopt,
4700
      std::optional<std::string_view> pathname = std::nullopt,
4701
      std::optional<std::string_view> search = std::nullopt,
4702
      std::optional<std::string_view> hash = std::nullopt);
4703
4704
  // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init
4705
  static tl::expected<std::string, errors> process_protocol(
4706
      std::string_view value, process_type type);
4707
4708
  // @see https://urlpattern.spec.whatwg.org/#process-username-for-init
4709
  static tl::expected<std::string, errors> process_username(
4710
      std::string_view value, process_type type);
4711
4712
  // @see https://urlpattern.spec.whatwg.org/#process-password-for-init
4713
  static tl::expected<std::string, errors> process_password(
4714
      std::string_view value, process_type type);
4715
4716
  // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init
4717
  static tl::expected<std::string, errors> process_hostname(
4718
      std::string_view value, process_type type);
4719
4720
  // @see https://urlpattern.spec.whatwg.org/#process-port-for-init
4721
  static tl::expected<std::string, errors> process_port(
4722
      std::string_view port, std::string_view protocol, process_type type);
4723
4724
  // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init
4725
  static tl::expected<std::string, errors> process_pathname(
4726
      std::string_view value, std::string_view protocol, process_type type);
4727
4728
  // @see https://urlpattern.spec.whatwg.org/#process-search-for-init
4729
  static tl::expected<std::string, errors> process_search(
4730
      std::string_view value, process_type type);
4731
4732
  // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init
4733
  static tl::expected<std::string, errors> process_hash(std::string_view value,
4734
                                                        process_type type);
4735
4736
#if ADA_TESTING
4737
  friend void PrintTo(const url_pattern_init& init, std::ostream* os) {
4738
    *os << "protocol: '" << init.protocol.value_or("undefined") << "', ";
4739
    *os << "username: '" << init.username.value_or("undefined") << "', ";
4740
    *os << "password: '" << init.password.value_or("undefined") << "', ";
4741
    *os << "hostname: '" << init.hostname.value_or("undefined") << "', ";
4742
    *os << "port: '" << init.port.value_or("undefined") << "', ";
4743
    *os << "pathname: '" << init.pathname.value_or("undefined") << "', ";
4744
    *os << "search: '" << init.search.value_or("undefined") << "', ";
4745
    *os << "hash: '" << init.hash.value_or("undefined") << "', ";
4746
    *os << "base_url: '" << init.base_url.value_or("undefined") << "', ";
4747
  }
4748
#endif  // ADA_TESTING
4749
4750
  bool operator==(const url_pattern_init&) const;
4751
  // If present, must be valid UTF-8.
4752
  std::optional<std::string> protocol{};
4753
  // If present, must be valid UTF-8.
4754
  std::optional<std::string> username{};
4755
  // If present, must be valid UTF-8.
4756
  std::optional<std::string> password{};
4757
  // If present, must be valid UTF-8.
4758
  std::optional<std::string> hostname{};
4759
  // If present, must be valid UTF-8.
4760
  std::optional<std::string> port{};
4761
  // If present, must be valid UTF-8.
4762
  std::optional<std::string> pathname{};
4763
  // If present, must be valid UTF-8.
4764
  std::optional<std::string> search{};
4765
  // If present, must be valid UTF-8.
4766
  std::optional<std::string> hash{};
4767
  // If present, must be valid UTF-8.
4768
  std::optional<std::string> base_url{};
4769
};
4770
}  // namespace ada
4771
#endif  // ADA_INCLUDE_URL_PATTERN
4772
#endif  // ADA_URL_PATTERN_INIT_H
4773
/* end file include/ada/url_pattern_init.h */
4774
4775
/** @private Forward declarations */
4776
namespace ada {
4777
struct url_aggregator;
4778
struct url;
4779
#if ADA_INCLUDE_URL_PATTERN
4780
template <url_pattern_regex::regex_concept regex_provider>
4781
class url_pattern;
4782
struct url_pattern_options;
4783
#endif  // ADA_INCLUDE_URL_PATTERN
4784
enum class errors : uint8_t;
4785
}  // namespace ada
4786
4787
/**
4788
 * @namespace ada::parser
4789
 * @brief Internal URL parsing implementation.
4790
 *
4791
 * Contains the core URL parsing algorithm as specified by the WHATWG URL
4792
 * Standard. These functions are used internally by `ada::parse()`.
4793
 */
4794
namespace ada::parser {
4795
/**
4796
 * Parses a URL string into a URL object.
4797
 *
4798
 * @tparam result_type The type of URL object to create (url or url_aggregator).
4799
 *
4800
 * @param user_input The URL string to parse (must be valid UTF-8).
4801
 * @param base_url Optional base URL for resolving relative URLs.
4802
 *
4803
 * @return The parsed URL object. Check `is_valid` to determine if parsing
4804
 *         succeeded.
4805
 *
4806
 * @see https://url.spec.whatwg.org/#concept-basic-url-parser
4807
 */
4808
template <typename result_type = url_aggregator>
4809
result_type parse_url(std::string_view user_input,
4810
                      const result_type* base_url = nullptr);
4811
4812
extern template url_aggregator parse_url<url_aggregator>(
4813
    std::string_view user_input, const url_aggregator* base_url);
4814
extern template url parse_url<url>(std::string_view user_input,
4815
                                   const url* base_url);
4816
4817
template <typename result_type = url_aggregator, bool store_values = true>
4818
result_type parse_url_impl(std::string_view user_input,
4819
                           const result_type* base_url = nullptr);
4820
4821
extern template url_aggregator parse_url_impl<url_aggregator, true>(
4822
    std::string_view user_input, const url_aggregator* base_url);
4823
extern template url_aggregator parse_url_impl<url_aggregator, false>(
4824
    std::string_view user_input, const url_aggregator* base_url);
4825
extern template url parse_url_impl<url, true>(std::string_view user_input,
4826
                                              const url* base_url);
4827
4828
/** @private */
4829
template <class result_type>
4830
bool try_parse_simple_absolute(std::string_view input, result_type& out);
4831
4832
/** @private */
4833
template <class result_type>
4834
bool finish_simple_absolute_with_port(std::string_view input, result_type& out,
4835
                                      ada::scheme::type scheme_type,
4836
                                      uint32_t protocol_end, size_t host_start,
4837
                                      size_t host_end, size_t host_len,
4838
                                      bool has_upper);
4839
4840
/** @private */
4841
template <class result_type>
4842
bool try_parse_simple_relative(std::string_view input, const result_type& base,
4843
                               result_type& out);
4844
4845
#if ADA_INCLUDE_URL_PATTERN
4846
template <url_pattern_regex::regex_concept regex_provider>
4847
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
4848
    std::variant<std::string_view, url_pattern_init>&& input,
4849
    const std::string_view* base_url, const url_pattern_options* options);
4850
#endif  // ADA_INCLUDE_URL_PATTERN
4851
4852
}  // namespace ada::parser
4853
4854
#endif  // ADA_PARSER_H
4855
/* end file include/ada/parser.h */
4856
/* begin file include/ada/parser-inl.h */
4857
/**
4858
 * @file parser-inl.h
4859
 */
4860
#ifndef ADA_PARSER_INL_H
4861
#define ADA_PARSER_INL_H
4862
4863
/* begin file include/ada/url_pattern.h */
4864
/**
4865
 * @file url_pattern.h
4866
 * @brief URLPattern API implementation.
4867
 *
4868
 * This header provides the URLPattern API as specified by the WHATWG URL
4869
 * Pattern Standard. URLPattern allows matching URLs against patterns with
4870
 * wildcards and named groups, similar to how regular expressions match strings.
4871
 *
4872
 * @see https://urlpattern.spec.whatwg.org/
4873
 * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
4874
 */
4875
#ifndef ADA_URL_PATTERN_H
4876
#define ADA_URL_PATTERN_H
4877
4878
/* begin file include/ada/implementation.h */
4879
/**
4880
 * @file implementation.h
4881
 * @brief User-facing functions for URL parsing and manipulation.
4882
 *
4883
 * This header provides the primary public API for parsing URLs in Ada.
4884
 * It includes the main `ada::parse()` function which is the recommended
4885
 * entry point for most users.
4886
 *
4887
 * @see https://url.spec.whatwg.org/#api
4888
 */
4889
#ifndef ADA_IMPLEMENTATION_H
4890
#define ADA_IMPLEMENTATION_H
4891
4892
#include <optional>
4893
#include <span>
4894
#include <string>
4895
#include <string_view>
4896
4897
/* begin file include/ada/url.h */
4898
/**
4899
 * @file url.h
4900
 * @brief Declaration for the `ada::url` class.
4901
 *
4902
 * This file contains the `ada::url` struct which represents a parsed URL
4903
 * using separate `std::string` instances for each component. This
4904
 * representation is more flexible but uses more memory than `url_aggregator`.
4905
 *
4906
 * @see url_aggregator.h for a more memory-efficient alternative
4907
 */
4908
#ifndef ADA_URL_H
4909
#define ADA_URL_H
4910
4911
#include <algorithm>
4912
#include <optional>
4913
#include <ostream>
4914
#include <string>
4915
#include <string_view>
4916
4917
/* begin file include/ada/url_components.h */
4918
/**
4919
 * @file url_components.h
4920
 * @brief URL component offset representation for url_aggregator.
4921
 *
4922
 * This file defines the `url_components` struct which stores byte offsets
4923
 * into a URL string buffer. It is used internally by `url_aggregator` to
4924
 * efficiently locate URL components without storing separate strings.
4925
 */
4926
#ifndef ADA_URL_COMPONENTS_H
4927
#define ADA_URL_COMPONENTS_H
4928
4929
namespace ada {
4930
4931
/**
4932
 * @brief Stores byte offsets for URL components within a buffer.
4933
 *
4934
 * The `url_components` struct uses 32-bit offsets to track the boundaries
4935
 * of each URL component within a single string buffer. This enables efficient
4936
 * component extraction without additional memory allocations.
4937
 *
4938
 * Component layout in a URL:
4939
 * ```
4940
 * https://user:pass@example.com:1234/foo/bar?baz#quux
4941
 *       |     |    |          | ^^^^|       |   |
4942
 *       |     |    |          | |   |       |   `----- hash_start
4943
 *       |     |    |          | |   |       `--------- search_start
4944
 *       |     |    |          | |   `----------------- pathname_start
4945
 *       |     |    |          | `--------------------- port
4946
 *       |     |    |          `----------------------- host_end
4947
 *       |     |    `---------------------------------- host_start
4948
 *       |     `--------------------------------------- username_end
4949
 *       `--------------------------------------------- protocol_end
4950
 * ```
4951
 *
4952
 * @note The 32-bit offsets limit URLs to 4GB in length.
4953
 * @note A value of `omitted` (UINT32_MAX) indicates the component is not
4954
 * present.
4955
 */
4956
struct url_components {
4957
  /** Sentinel value indicating a component is not present. */
4958
  constexpr static uint32_t omitted = uint32_t(-1);
4959
4960
242k
  url_components() = default;
4961
  url_components(const url_components& u) = default;
4962
  url_components(url_components&& u) noexcept = default;
4963
  url_components& operator=(url_components&& u) noexcept = default;
4964
  url_components& operator=(const url_components& u) = default;
4965
  ~url_components() = default;
4966
4967
  /** Offset of the end of the protocol/scheme (position of ':'). */
4968
  uint32_t protocol_end{0};
4969
4970
  /**
4971
   * Offset of the end of the username.
4972
   * Initialized to 0 (not `omitted`) to simplify username/password getters.
4973
   */
4974
  uint32_t username_end{0};
4975
4976
  /** Offset of the start of the host. */
4977
  uint32_t host_start{0};
4978
4979
  /** Offset of the end of the host. */
4980
  uint32_t host_end{0};
4981
4982
  /** Port number, or `omitted` if no port is specified. */
4983
  uint32_t port{omitted};
4984
4985
  /** Offset of the start of the pathname. */
4986
  uint32_t pathname_start{0};
4987
4988
  /** Offset of the '?' starting the query, or `omitted` if no query. */
4989
  uint32_t search_start{omitted};
4990
4991
  /** Offset of the '#' starting the fragment, or `omitted` if no fragment. */
4992
  uint32_t hash_start{omitted};
4993
4994
  /**
4995
   * Validates that offsets are in ascending order and consistent.
4996
   * Useful for debugging to detect internal corruption.
4997
   * @return `true` if offsets are consistent, `false` otherwise.
4998
   */
4999
  [[nodiscard]] constexpr bool check_offset_consistency() const noexcept;
5000
5001
  /**
5002
   * Returns a JSON string representation of the offsets for debugging.
5003
   * @return A JSON-formatted string with all offset values.
5004
   */
5005
  [[nodiscard]] std::string to_string() const;
5006
5007
};  // struct url_components
5008
}  // namespace ada
5009
#endif
5010
/* end file include/ada/url_components.h */
5011
5012
namespace ada {
5013
5014
struct url_aggregator;
5015
5016
// namespace parser {
5017
// template <typename result_type>
5018
// result_type parse_url(std::string_view user_input,
5019
//                       const result_type* base_url = nullptr);
5020
// template <typename result_type, bool store_values>
5021
// result_type parse_url_impl(std::string_view user_input,
5022
//                            const result_type* base_url = nullptr);
5023
// }
5024
5025
/**
5026
 * @brief Represents a parsed URL with individual string components.
5027
 *
5028
 * The `url` struct stores each URL component (scheme, username, password,
5029
 * host, port, path, query, fragment) as a separate `std::string`. This
5030
 * provides flexibility but incurs more memory allocations compared to
5031
 * `url_aggregator`.
5032
 *
5033
 * **When to use `ada::url`:**
5034
 * - When you need to frequently modify individual URL components
5035
 * - When you want independent ownership of component strings
5036
 *
5037
 * **When to use `ada::url_aggregator` instead:**
5038
 * - For read-mostly operations on parsed URLs
5039
 * - When memory efficiency is important
5040
 * - When you only need string_view access to components
5041
 *
5042
 * @note This type is returned when parsing with `ada::parse<ada::url>()`.
5043
 *       By default, `ada::parse()` returns `ada::url_aggregator`.
5044
 *
5045
 * @see url_aggregator For a more memory-efficient URL representation
5046
 * @see https://url.spec.whatwg.org/#url-representation
5047
 */
5048
struct url : url_base {
5049
126k
  url() = default;
5050
54.2k
  url(const url& u) = default;
5051
95.7k
  url(url&& u) noexcept = default;
5052
9.87k
  url& operator=(url&& u) noexcept = default;
5053
14.4k
  url& operator=(const url& u) = default;
5054
276k
  ~url() override = default;
5055
5056
  // Fields are ordered so that the most frequently accessed components
5057
  // tend to occupy earlier cache lines and remain close together in memory.
5058
  //
5059
  // Note: The exact object layout (including cache-line boundaries, byte
5060
  // offsets, and member sizes) is implementation- and platform-dependent.
5061
  // This ordering expresses an intent for better cache locality but does not
5062
  // guarantee any specific in-memory layout.
5063
5064
  /**
5065
   * @private
5066
   * A URL's host is null or a host. It is initially null.
5067
   */
5068
  std::optional<std::string> host{};
5069
5070
  /**
5071
   * @private
5072
   * A URL's path is either an ASCII string or a list of zero or more ASCII
5073
   * strings, usually identifying a location.
5074
   */
5075
  std::string path{};
5076
5077
  /**
5078
   * @private
5079
   * A URL's query is either null or an ASCII string. It is initially null.
5080
   */
5081
  std::optional<std::string> query{};
5082
5083
  /**
5084
   * @private
5085
   * A URL's fragment is either null or an ASCII string that can be used for
5086
   * further processing on the resource the URL's other components identify. It
5087
   * is initially null.
5088
   */
5089
  std::optional<std::string> hash{};
5090
5091
  /**
5092
   * @private
5093
   * A URL's port is either null or a 16-bit unsigned integer that identifies a
5094
   * networking port. It is initially null.
5095
   */
5096
  std::optional<uint16_t> port{};
5097
5098
  /**
5099
   * @private
5100
   * A URL's username is an ASCII string identifying a username. It is initially
5101
   * the empty string.
5102
   */
5103
  std::string username{};
5104
5105
  /**
5106
   * @private
5107
   * A URL's password is an ASCII string identifying a password. It is initially
5108
   * the empty string.
5109
   */
5110
  std::string password{};
5111
5112
  /**
5113
   * Checks if the URL has an empty hostname (host is set but empty string).
5114
   * @return `true` if host exists but is empty, `false` otherwise.
5115
   */
5116
  [[nodiscard]] inline bool has_empty_hostname() const noexcept;
5117
5118
  /**
5119
   * Checks if the URL has a non-default port explicitly specified.
5120
   * @return `true` if a port is present, `false` otherwise.
5121
   */
5122
  [[nodiscard]] inline bool has_port() const noexcept;
5123
5124
  /**
5125
   * Checks if the URL has a hostname (including empty hostnames).
5126
   * @return `true` if host is present, `false` otherwise.
5127
   */
5128
  [[nodiscard]] inline bool has_hostname() const noexcept;
5129
5130
  /**
5131
   * Validates whether the hostname is a valid domain according to RFC 1034.
5132
   * Checks that the domain and its labels have valid lengths (max 255 octets
5133
   * total, max 63 octets per label).
5134
   * @return `true` if the domain is valid, `false` otherwise.
5135
   */
5136
  [[nodiscard]] bool has_valid_domain() const noexcept override;
5137
5138
  /**
5139
   * Returns a JSON string representation of this URL for debugging.
5140
   * @return A JSON-formatted string with all URL components.
5141
   */
5142
  [[nodiscard]] std::string to_string() const override;
5143
5144
  /**
5145
   * Returns the full serialized URL (the href).
5146
   * @return The complete URL string (allocates a new string).
5147
   * @see https://url.spec.whatwg.org/#dom-url-href
5148
   */
5149
  [[nodiscard]] ada_really_inline std::string get_href() const;
5150
5151
  /**
5152
   * Returns the byte length of the serialized URL without allocating a string.
5153
   * @return Size of the href in bytes.
5154
   */
5155
  [[nodiscard]] size_t get_href_size() const noexcept;
5156
5157
  /**
5158
   * Returns the URL's origin as a string (scheme + host + port for special
5159
   * URLs).
5160
   * @return A newly allocated string containing the serialized origin.
5161
   * @see https://url.spec.whatwg.org/#concept-url-origin
5162
   */
5163
  [[nodiscard]] std::string get_origin() const override;
5164
5165
  /**
5166
   * Returns the URL's scheme followed by a colon (e.g., "https:").
5167
   * @return A newly allocated string with the protocol.
5168
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5169
   */
5170
  [[nodiscard]] std::string get_protocol() const;
5171
5172
  /**
5173
   * Returns the URL's host and port (e.g., "example.com:8080").
5174
   * If no port is set, returns just the host. Returns empty string if no host.
5175
   * @return A newly allocated string with host:port.
5176
   * @see https://url.spec.whatwg.org/#dom-url-host
5177
   */
5178
  [[nodiscard]] std::string get_host() const;
5179
5180
  /**
5181
   * Returns the URL's hostname (without port).
5182
   * Returns empty string if no host is set.
5183
   * @return A newly allocated string with the hostname.
5184
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5185
   */
5186
  [[nodiscard]] std::string get_hostname() const;
5187
5188
  /**
5189
   * Returns the URL's path component.
5190
   * @return A string_view pointing to the path.
5191
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5192
   */
5193
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept;
5194
5195
  /**
5196
   * Returns the byte length of the pathname without creating a string.
5197
   * @return Size of the pathname in bytes.
5198
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5199
   */
5200
  [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
5201
5202
  /**
5203
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
5204
   * Returns empty string if no query is set.
5205
   * @return A newly allocated string with the search/query.
5206
   * @see https://url.spec.whatwg.org/#dom-url-search
5207
   */
5208
  [[nodiscard]] std::string get_search() const;
5209
5210
  /**
5211
   * Returns the URL's username component.
5212
   * @return A constant reference to the username string.
5213
   * @see https://url.spec.whatwg.org/#dom-url-username
5214
   */
5215
  [[nodiscard]] const std::string& get_username() const noexcept;
5216
5217
  /**
5218
   * Sets the URL's username, percent-encoding special characters.
5219
   * @param input The new username value.
5220
   * @return `true` on success, `false` if the URL cannot have credentials.
5221
   * @see https://url.spec.whatwg.org/#dom-url-username
5222
   */
5223
  bool set_username(std::string_view input);
5224
5225
  /**
5226
   * Sets the URL's password, percent-encoding special characters.
5227
   * @param input The new password value.
5228
   * @return `true` on success, `false` if the URL cannot have credentials.
5229
   * @see https://url.spec.whatwg.org/#dom-url-password
5230
   */
5231
  bool set_password(std::string_view input);
5232
5233
  /**
5234
   * Sets the URL's port from a string (e.g., "8080").
5235
   * @param input The port string. Empty string removes the port.
5236
   * @return `true` on success, `false` if the URL cannot have a port.
5237
   * @see https://url.spec.whatwg.org/#dom-url-port
5238
   */
5239
  bool set_port(std::string_view input);
5240
5241
  /**
5242
   * Sets the URL's fragment/hash (the part after '#').
5243
   * @param input The new hash value (with or without leading '#').
5244
   * @see https://url.spec.whatwg.org/#dom-url-hash
5245
   */
5246
  void set_hash(std::string_view input);
5247
5248
  /**
5249
   * Sets the URL's query string (the part after '?').
5250
   * @param input The new query value (with or without leading '?').
5251
   * @see https://url.spec.whatwg.org/#dom-url-search
5252
   */
5253
  void set_search(std::string_view input);
5254
5255
  /**
5256
   * Sets the URL's pathname.
5257
   * @param input The new path value.
5258
   * @return `true` on success, `false` if the URL has an opaque path.
5259
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5260
   */
5261
  bool set_pathname(std::string_view input);
5262
5263
  /**
5264
   * Sets the URL's host (hostname and optionally port).
5265
   * @param input The new host value (e.g., "example.com:8080").
5266
   * @return `true` on success, `false` if parsing fails.
5267
   * @see https://url.spec.whatwg.org/#dom-url-host
5268
   */
5269
  bool set_host(std::string_view input);
5270
5271
  /**
5272
   * Sets the URL's hostname (without port).
5273
   * @param input The new hostname value.
5274
   * @return `true` on success, `false` if parsing fails.
5275
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5276
   */
5277
  bool set_hostname(std::string_view input);
5278
5279
  /**
5280
   * Sets the URL's protocol/scheme.
5281
   * @param input The new protocol (with or without trailing ':').
5282
   * @return `true` on success, `false` if the scheme is invalid.
5283
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5284
   */
5285
  bool set_protocol(std::string_view input);
5286
5287
  /**
5288
   * Replaces the entire URL by parsing a new href string.
5289
   * @param input The new URL string to parse.
5290
   * @return `true` on success, `false` if parsing fails.
5291
   * @see https://url.spec.whatwg.org/#dom-url-href
5292
   */
5293
  bool set_href(std::string_view input);
5294
5295
  /**
5296
   * Returns the URL's password component.
5297
   * @return A constant reference to the password string.
5298
   * @see https://url.spec.whatwg.org/#dom-url-password
5299
   */
5300
  [[nodiscard]] const std::string& get_password() const noexcept;
5301
5302
  /**
5303
   * Returns the URL's port as a string (e.g., "8080").
5304
   * Returns empty string if no port is set.
5305
   * @return A newly allocated string with the port.
5306
   * @see https://url.spec.whatwg.org/#dom-url-port
5307
   */
5308
  [[nodiscard]] std::string get_port() const;
5309
5310
  /**
5311
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
5312
   * Returns empty string if no fragment is set.
5313
   * @return A newly allocated string with the hash.
5314
   * @see https://url.spec.whatwg.org/#dom-url-hash
5315
   */
5316
  [[nodiscard]] std::string get_hash() const;
5317
5318
  /**
5319
   * Checks if the URL has credentials (non-empty username or password).
5320
   * @return `true` if username or password is non-empty, `false` otherwise.
5321
   */
5322
  [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5323
5324
  /**
5325
   * Returns the URL component offsets for efficient serialization.
5326
   *
5327
   * The components represent byte offsets into the serialized URL:
5328
   * ```
5329
   * https://user:pass@example.com:1234/foo/bar?baz#quux
5330
   *       |     |    |          | ^^^^|       |   |
5331
   *       |     |    |          | |   |       |   `----- hash_start
5332
   *       |     |    |          | |   |       `--------- search_start
5333
   *       |     |    |          | |   `----------------- pathname_start
5334
   *       |     |    |          | `--------------------- port
5335
   *       |     |    |          `----------------------- host_end
5336
   *       |     |    `---------------------------------- host_start
5337
   *       |     `--------------------------------------- username_end
5338
   *       `--------------------------------------------- protocol_end
5339
   * ```
5340
   * @return A newly constructed url_components struct.
5341
   * @see https://github.com/servo/rust-url
5342
   */
5343
  [[nodiscard]] ada_really_inline ada::url_components get_components() const;
5344
5345
  /**
5346
   * Checks if the URL has a fragment/hash component.
5347
   * @return `true` if hash is present, `false` otherwise.
5348
   */
5349
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
5350
5351
  /**
5352
   * Checks if the URL has a query/search component.
5353
   * @return `true` if query is present, `false` otherwise.
5354
   */
5355
  [[nodiscard]] constexpr bool has_search() const noexcept override;
5356
5357
 private:
5358
  friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5359
                                                   const ada::url*);
5360
  friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5361
      std::string_view, const ada::url_aggregator*);
5362
  friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5363
      ada::url& url);
5364
5365
  friend ada::url ada::parser::parse_url_impl<ada::url, true>(std::string_view,
5366
                                                              const ada::url*);
5367
  friend ada::url_aggregator ada::parser::parse_url_impl<
5368
      ada::url_aggregator, true>(std::string_view, const ada::url_aggregator*);
5369
  friend ada::url_aggregator ada::parser::parse_url_impl<
5370
      ada::url_aggregator, false>(std::string_view, const ada::url_aggregator*);
5371
  template <class result_type>
5372
  friend bool ada::parser::try_parse_simple_absolute(std::string_view,
5373
                                                     result_type&);
5374
  template <class result_type>
5375
  friend bool ada::parser::finish_simple_absolute_with_port(
5376
      std::string_view, result_type&, ada::scheme::type, uint32_t, size_t,
5377
      size_t, size_t, bool);
5378
  template <class result_type>
5379
  friend bool ada::parser::try_parse_simple_relative(std::string_view,
5380
                                                     const result_type&,
5381
                                                     result_type&);
5382
5383
  inline void update_unencoded_base_hash(std::string_view input);
5384
  inline void update_base_hostname(std::string_view input);
5385
  inline void update_base_search(std::string_view input,
5386
                                 const uint8_t query_percent_encode_set[]);
5387
  inline void update_base_search(std::optional<std::string>&& input);
5388
  inline void update_base_pathname(std::string_view input);
5389
  inline void update_base_username(std::string_view input);
5390
  inline void update_base_password(std::string_view input);
5391
  inline void update_base_port(std::optional<uint16_t> input);
5392
5393
  /**
5394
   * Returns true if growing href by input_len (worst case) could exceed
5395
   * get_max_input_length(), meaning a rollback snapshot is actually needed.
5396
   */
5397
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
5398
5399
  /**
5400
   * Sets the host or hostname according to override condition.
5401
   * Return true on success.
5402
   * @see https://url.spec.whatwg.org/#hostname-state
5403
   */
5404
  template <bool override_hostname = false>
5405
  bool set_host_or_hostname(std::string_view input);
5406
5407
  /**
5408
   * Return true on success.
5409
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5410
   */
5411
  [[nodiscard]] bool parse_ipv4(std::string_view input);
5412
5413
  /**
5414
   * Return true on success.
5415
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5416
   */
5417
  [[nodiscard]] bool parse_ipv6(std::string_view input);
5418
5419
  /**
5420
   * Return true on success.
5421
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5422
   */
5423
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
5424
5425
  /**
5426
   * A URL's scheme is an ASCII string that identifies the type of URL and can
5427
   * be used to dispatch a URL for further processing after parsing. It is
5428
   * initially the empty string. We only set non_special_scheme when the scheme
5429
   * is non-special, otherwise we avoid constructing string.
5430
   *
5431
   * Special schemes are stored in ada::scheme::details::is_special_list so we
5432
   * typically do not need to store them in each url instance.
5433
   */
5434
  std::string non_special_scheme{};
5435
5436
  /**
5437
   * A URL cannot have a username/password/port if its host is null or the empty
5438
   * string, or its scheme is "file".
5439
   */
5440
  [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5441
5442
  ada_really_inline size_t parse_port(
5443
      std::string_view view, bool check_trailing_content) noexcept override;
5444
5445
2.73k
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5446
2.73k
    return this->parse_port(view, false);
5447
2.73k
  }
5448
5449
  /**
5450
   * Parse the host from the provided input. We assume that
5451
   * the input does not contain spaces or tabs. Control
5452
   * characters and spaces are not trimmed (they should have
5453
   * been removed if needed).
5454
   * Return true on success.
5455
   * @see https://url.spec.whatwg.org/#host-parsing
5456
   */
5457
  [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5458
5459
  template <bool has_state_override = false>
5460
  [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5461
5462
  constexpr void clear_pathname() override;
5463
  constexpr void clear_search() override;
5464
  constexpr void set_protocol_as_file();
5465
5466
  /**
5467
   * Parse the path from the provided input.
5468
   * Return true on success. Control characters not
5469
   * trimmed from the ends (they should have
5470
   * been removed if needed).
5471
   *
5472
   * The input is expected to be UTF-8.
5473
   *
5474
   * @see https://url.spec.whatwg.org/
5475
   */
5476
  ada_really_inline void parse_path(std::string_view input);
5477
5478
  /**
5479
   * Set the scheme for this URL. The provided scheme should be a valid
5480
   * scheme string, be lower-cased, not contain spaces or tabs. It should
5481
   * have no spurious trailing or leading content.
5482
   */
5483
  inline void set_scheme(std::string&& new_scheme) noexcept;
5484
5485
  /**
5486
   * Take the scheme from another URL. The scheme string is moved from the
5487
   * provided url.
5488
   */
5489
  constexpr void copy_scheme(ada::url&& u);
5490
5491
  /**
5492
   * Take the scheme from another URL. The scheme string is copied from the
5493
   * provided url.
5494
   */
5495
  constexpr void copy_scheme(const ada::url& u);
5496
5497
};  // struct url
5498
5499
inline std::ostream& operator<<(std::ostream& out, const ada::url& u);
5500
}  // namespace ada
5501
5502
#endif  // ADA_URL_H
5503
/* end file include/ada/url.h */
5504
5505
namespace ada {
5506
5507
/**
5508
 * Result type for URL parsing operations.
5509
 *
5510
 * Uses `tl::expected` to represent either a successfully parsed URL or an
5511
 * error. This allows for exception-free error handling.
5512
 *
5513
 * @tparam result_type The URL type to return (default: `ada::url_aggregator`)
5514
 *
5515
 * @example
5516
 * ```cpp
5517
 * ada::result<ada::url_aggregator> result = ada::parse("https://example.com");
5518
 * if (result) {
5519
 *     // Success: use result.value() or *result
5520
 * } else {
5521
 *     // Error: handle result.error()
5522
 * }
5523
 * ```
5524
 */
5525
template <class result_type = ada::url_aggregator>
5526
using result = tl::expected<result_type, ada::errors>;
5527
5528
/**
5529
 * Parses a URL string according to the WHATWG URL Standard.
5530
 *
5531
 * This is the main entry point for URL parsing in Ada. The function takes
5532
 * a string input and optionally a base URL for resolving relative URLs.
5533
 *
5534
 * @tparam result_type The URL type to return. Can be either `ada::url` or
5535
 *         `ada::url_aggregator` (default). The `url_aggregator` type is more
5536
 *         memory-efficient as it stores components as offsets into a single
5537
 *         buffer.
5538
 *
5539
 * @param input The URL string to parse. Must be valid ASCII or UTF-8 encoded.
5540
 *        Leading and trailing whitespace is automatically trimmed.
5541
 * @param base_url Optional pointer to a base URL for resolving relative URLs.
5542
 *        If nullptr (default), only absolute URLs can be parsed successfully.
5543
 *
5544
 * @return A `result<result_type>` containing either the parsed URL on success,
5545
 *         or an error code on failure. Use the boolean conversion or
5546
 *         `has_value()` to check for success.
5547
 *
5548
 * @note The parser is fully compliant with the WHATWG URL Standard.
5549
 *
5550
 * Parsing fails if the input or the resulting normalized URL exceeds
5551
 * `get_max_input_length()` bytes (default ~4 GB, configurable via
5552
 * `set_max_input_length()`). This accounts for percent-encoding expansion:
5553
 * a short input that normalizes into a long URL is still rejected.
5554
 *
5555
 * @example
5556
 * ```cpp
5557
 * // Parse an absolute URL
5558
 * auto url = ada::parse("https://user:pass@example.com:8080/path?query#hash");
5559
 * if (url) {
5560
 *     std::cout << url->get_hostname(); // "example.com"
5561
 *     std::cout << url->get_pathname(); // "/path"
5562
 * }
5563
 *
5564
 * // Parse a relative URL with a base
5565
 * auto base = ada::parse("https://example.com/dir/");
5566
 * if (base) {
5567
 *     auto relative = ada::parse("../other/page", &*base);
5568
 *     if (relative) {
5569
 *         std::cout << relative->get_href(); //
5570
 * "https://example.com/other/page"
5571
 *     }
5572
 * }
5573
 * ```
5574
 *
5575
 * @see https://url.spec.whatwg.org/#url-parsing
5576
 */
5577
template <class result_type = ada::url_aggregator>
5578
ada_warn_unused ada::result<result_type> parse(
5579
    std::string_view input, const result_type* base_url = nullptr);
5580
5581
extern template ada::result<url> parse<url>(std::string_view input,
5582
                                            const url* base_url);
5583
extern template ada::result<url_aggregator> parse<url_aggregator>(
5584
    std::string_view input, const url_aggregator* base_url);
5585
5586
/**
5587
 * Checks whether a URL string can be successfully parsed.
5588
 *
5589
 * Equivalent to `parse(input, base).has_value()` for every input, including
5590
 * when `set_max_input_length` rejects a normalized href that exceeds the limit.
5591
 *
5592
 * @param input The URL string to validate. Must be valid ASCII or UTF-8.
5593
 * @param base_input Optional base URL string for relative inputs.
5594
 * @return `true` if parsing would succeed, `false` otherwise.
5595
 * @see https://url.spec.whatwg.org/#dom-url-canparse
5596
 */
5597
bool can_parse(std::string_view input,
5598
               const std::string_view* base_input = nullptr);
5599
5600
#if ADA_INCLUDE_URL_PATTERN
5601
/**
5602
 * Parses a URL pattern according to the URLPattern specification.
5603
 *
5604
 * URL patterns provide a syntax for matching URLs against patterns, similar
5605
 * to how regular expressions match strings. This is useful for routing and
5606
 * URL-based dispatching.
5607
 *
5608
 * @tparam regex_provider The regex implementation to use for pattern matching.
5609
 *
5610
 * @param input Either a URL pattern string (valid UTF-8) or a URLPatternInit
5611
 *        struct specifying individual component patterns.
5612
 * @param base_url Optional pointer to a base URL string (valid UTF-8) for
5613
 *        resolving relative patterns.
5614
 * @param options Optional pointer to configuration options (e.g., ignore_case).
5615
 *
5616
 * @return A `tl::expected` containing either the parsed url_pattern on success,
5617
 *         or an error code on failure.
5618
 *
5619
 * @see https://urlpattern.spec.whatwg.org
5620
 */
5621
template <url_pattern_regex::regex_concept regex_provider>
5622
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
5623
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
5624
                  const std::string_view* base_url = nullptr,
5625
                  const url_pattern_options* options = nullptr);
5626
5627
template <url_pattern_regex::regex_concept regex_provider>
5628
class url_pattern_list;
5629
5630
/**
5631
 * Compiles a set of URLPattern pathname patterns into an ada::url_pattern_list
5632
 * (experimental): one structure that answers "which route matches this
5633
 * pathname, and what are its group values?" without a loop over the
5634
 * patterns and without executing regular expressions for routes written in
5635
 * the static / ":param" / "*" subset.
5636
 *
5637
 * @tparam regex_provider The regex implementation, as for parse_url_pattern;
5638
 *         it is used only for routes that need URLPattern regexp semantics.
5639
 *
5640
 * @param pathname_patterns URLPattern pathname patterns (valid UTF-8), for
5641
 *        example "/users/:id". The index of a pattern is the route index
5642
 *        reported by url_pattern_list::match; duplicates are allowed.
5643
 * @param base_url Optional pointer to a base URL string (valid UTF-8): each
5644
 *        pattern is then processed as the pathname of a URLPatternInit with
5645
 *        that base URL (relative patterns resolve against its path).
5646
 * @param options Optional pointer to configuration options (ignore_case),
5647
 *        applied to every route as parse_url_pattern applies them.
5648
 *
5649
 * @return A `tl::expected` containing either the compiled list on success,
5650
 *         or an error code when a pattern is not a valid URLPattern pathname
5651
 *         pattern (errors::type_error, as the URLPattern constructor).
5652
 *
5653
 * @see https://github.com/whatwg/urlpattern/issues/166
5654
 */
5655
template <url_pattern_regex::regex_concept regex_provider>
5656
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
5657
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
5658
                       const std::string_view* base_url = nullptr,
5659
                       const url_pattern_options* options = nullptr);
5660
5661
/**
5662
 * Compiles the pathname components of existing url_pattern objects into an
5663
 * ada::url_pattern_list (experimental). Only the pathname component of each
5664
 * pattern is used; routes that need the regex provider reuse the pattern's
5665
 * already compiled pathname component. All patterns must share the same
5666
 * ignore_case setting (errors::type_error otherwise); the pathname is
5667
 * compiled as for a special-scheme URL.
5668
 */
5669
template <url_pattern_regex::regex_concept regex_provider>
5670
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
5671
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns);
5672
#endif  // ADA_INCLUDE_URL_PATTERN
5673
5674
/**
5675
 * Converts a file system path to a file:// URL.
5676
 *
5677
 * Creates a properly formatted file URL from a local file system path.
5678
 * Handles platform-specific path separators and percent-encoding.
5679
 *
5680
 * Respects `get_max_input_length()`: if the input path or the resulting
5681
 * `file://` href would exceed the limit, returns an empty string.
5682
 *
5683
 * @param path The file system path to convert. Must be valid ASCII or UTF-8.
5684
 *
5685
 * @return A file:// URL string representing the given path, or empty on
5686
 *         length-limit rejection.
5687
 */
5688
std::string href_from_file(std::string_view path);
5689
5690
/**
5691
 * Sets the maximum allowed length for URLs.
5692
 *
5693
 * Both the raw input and the resulting normalized URL (the href) are checked
5694
 * against this limit. Parsing or setter calls that would produce a URL
5695
 * exceeding this length are rejected. The same limit also applies to
5696
 * `href_from_file` and to query strings passed to `url_search_params`
5697
 * construction / `reset`. The value must fit in a uint32_t.
5698
 * The default is std::numeric_limits<uint32_t>::max() (approximately 4 GB).
5699
 *
5700
 * @param length The new maximum URL length in bytes.
5701
 */
5702
void set_max_input_length(uint32_t length);
5703
5704
/**
5705
 * Returns the current maximum allowed length for URLs.
5706
 *
5707
 * @return The current maximum URL length in bytes.
5708
 */
5709
uint32_t get_max_input_length();
5710
5711
}  // namespace ada
5712
5713
#endif  // ADA_IMPLEMENTATION_H
5714
/* end file include/ada/implementation.h */
5715
5716
#include <ostream>
5717
#include <string>
5718
#include <string_view>
5719
#include <unordered_map>
5720
#include <variant>
5721
#include <vector>
5722
5723
#if ADA_TESTING
5724
#include <iostream>
5725
#endif  // ADA_TESTING
5726
5727
#if ADA_INCLUDE_URL_PATTERN
5728
namespace ada {
5729
5730
enum class url_pattern_part_type : uint8_t {
5731
  // The part represents a simple fixed text string.
5732
  FIXED_TEXT,
5733
  // The part represents a matching group with a custom regular expression.
5734
  REGEXP,
5735
  // The part represents a matching group that matches code points up to the
5736
  // next separator code point. This is typically used for a named group like
5737
  // ":foo" that does not have a custom regular expression.
5738
  SEGMENT_WILDCARD,
5739
  // The part represents a matching group that greedily matches all code points.
5740
  // This is typically used for the "*" wildcard matching group.
5741
  FULL_WILDCARD,
5742
};
5743
5744
// Pattern type for fast-path matching optimization.
5745
// This allows skipping expensive regex evaluation for common simple patterns.
5746
enum class url_pattern_component_type : uint8_t {
5747
  // Pattern is "^$" - only matches empty string
5748
  EMPTY,
5749
  // Pattern is "^<literal>$" - exact string match (no regex needed)
5750
  EXACT_MATCH,
5751
  // Pattern is "^(.*)$" - matches anything (full wildcard)
5752
  FULL_WILDCARD,
5753
  // Pattern requires actual regex evaluation
5754
  REGEXP,
5755
};
5756
5757
enum class url_pattern_part_modifier : uint8_t {
5758
  // The part does not have a modifier.
5759
  none,
5760
  // The part has an optional modifier indicated by the U+003F (?) code point.
5761
  optional,
5762
  // The part has a "zero or more" modifier indicated by the U+002A (*) code
5763
  // point.
5764
  zero_or_more,
5765
  // The part has a "one or more" modifier indicated by the U+002B (+) code
5766
  // point.
5767
  one_or_more,
5768
};
5769
5770
// @see https://urlpattern.spec.whatwg.org/#part
5771
class url_pattern_part {
5772
 public:
5773
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5774
                   url_pattern_part_modifier _modifier)
5775
0
      : type(_type), value(std::move(_value)), modifier(_modifier) {}
5776
5777
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5778
                   url_pattern_part_modifier _modifier, std::string&& _name,
5779
                   std::string&& _prefix, std::string&& _suffix)
5780
0
      : type(_type),
5781
0
        value(std::move(_value)),
5782
0
        modifier(_modifier),
5783
0
        name(std::move(_name)),
5784
0
        prefix(std::move(_prefix)),
5785
0
        suffix(std::move(_suffix)) {}
5786
  // A part has an associated type, a string, which must be set upon creation.
5787
  url_pattern_part_type type;
5788
  // A part has an associated value, a string, which must be set upon creation.
5789
  std::string value;
5790
  // A part has an associated modifier a string, which must be set upon
5791
  // creation.
5792
  url_pattern_part_modifier modifier;
5793
  // A part has an associated name, a string, initially the empty string.
5794
  std::string name{};
5795
  // A part has an associated prefix, a string, initially the empty string.
5796
  std::string prefix{};
5797
  // A part has an associated suffix, a string, initially the empty string.
5798
  std::string suffix{};
5799
5800
  inline bool is_regexp() const noexcept;
5801
};
5802
5803
// @see https://urlpattern.spec.whatwg.org/#options-header
5804
struct url_pattern_compile_component_options {
5805
  url_pattern_compile_component_options() = default;
5806
  explicit url_pattern_compile_component_options(
5807
      std::optional<char> new_delimiter = std::nullopt,
5808
      std::optional<char> new_prefix = std::nullopt) noexcept
5809
6
      : delimiter(new_delimiter), prefix(new_prefix) {}
5810
5811
  inline std::string_view get_delimiter() const ada_warn_unused;
5812
  inline std::string_view get_prefix() const ada_warn_unused;
5813
5814
  // @see https://urlpattern.spec.whatwg.org/#options-ignore-case
5815
  bool ignore_case = false;
5816
5817
  static url_pattern_compile_component_options DEFAULT;
5818
  static url_pattern_compile_component_options HOSTNAME;
5819
  static url_pattern_compile_component_options PATHNAME;
5820
5821
 private:
5822
  // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point
5823
  std::optional<char> delimiter{};
5824
  // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point
5825
  std::optional<char> prefix{};
5826
};
5827
5828
// The default options is an options struct with delimiter code point set to
5829
// the empty string and prefix code point set to the empty string.
5830
inline url_pattern_compile_component_options
5831
    url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt);
5832
5833
// The hostname options is an options struct with delimiter code point set
5834
// "." and prefix code point set to the empty string.
5835
inline url_pattern_compile_component_options
5836
    url_pattern_compile_component_options::HOSTNAME('.', std::nullopt);
5837
5838
// The pathname options is an options struct with delimiter code point set
5839
// "/" and prefix code point set to "/".
5840
inline url_pattern_compile_component_options
5841
    url_pattern_compile_component_options::PATHNAME('/', '/');
5842
5843
// A struct providing the URLPattern matching results for a single
5844
// URL component. The URLPatternComponentResult is only ever used
5845
// as a member attribute of a URLPatternResult struct. The
5846
// URLPatternComponentResult API is defined as part of the URLPattern
5847
// specification.
5848
struct url_pattern_component_result {
5849
  std::string input;
5850
  std::unordered_map<std::string, std::optional<std::string>> groups;
5851
5852
  bool operator==(const url_pattern_component_result&) const;
5853
5854
#if ADA_TESTING
5855
  friend void PrintTo(const url_pattern_component_result& result,
5856
                      std::ostream* os) {
5857
    *os << "input: '" << result.input << "', group: ";
5858
    for (const auto& group : result.groups) {
5859
      *os << "(" << group.first << ", " << group.second.value_or("undefined")
5860
          << ") ";
5861
    }
5862
  }
5863
#endif  // ADA_TESTING
5864
};
5865
5866
template <url_pattern_regex::regex_concept regex_provider>
5867
class url_pattern_component {
5868
 public:
5869
  url_pattern_component() = default;
5870
5871
  // This function explicitly takes a std::string because it is moved.
5872
  // To avoid unnecessary copy, move each value while calling the constructor.
5873
  url_pattern_component(std::string&& new_pattern,
5874
                        typename regex_provider::regex_type&& new_regexp,
5875
                        std::vector<std::string>&& new_group_name_list,
5876
                        bool new_has_regexp_groups,
5877
                        url_pattern_component_type new_type,
5878
                        std::string&& new_exact_match_value = {})
5879
      : regexp(std::move(new_regexp)),
5880
        pattern(std::move(new_pattern)),
5881
        group_name_list(std::move(new_group_name_list)),
5882
        exact_match_value(std::move(new_exact_match_value)),
5883
        has_regexp_groups(new_has_regexp_groups),
5884
        type(new_type) {}
5885
5886
  // @see https://urlpattern.spec.whatwg.org/#compile-a-component
5887
  template <url_pattern_encoding_callback F>
5888
  static tl::expected<url_pattern_component, errors> compile(
5889
      std::string_view input, F& encoding_callback,
5890
      url_pattern_compile_component_options& options);
5891
5892
  // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result
5893
  url_pattern_component_result create_component_match_result(
5894
      std::string&& input,
5895
      std::vector<std::optional<std::string>>&& exec_result);
5896
5897
  // Fast path test that returns true/false without constructing result groups.
5898
  // Uses cached pattern type to skip regex evaluation for simple patterns.
5899
  bool fast_test(std::string_view input) const noexcept;
5900
5901
  // Fast path match that returns capture groups without regex for simple
5902
  // patterns. Returns nullopt if pattern doesn't match, otherwise returns
5903
  // capture groups.
5904
  std::optional<std::vector<std::optional<std::string>>> fast_match(
5905
      std::string_view input) const;
5906
5907
#if ADA_TESTING
5908
  friend void PrintTo(const url_pattern_component& component,
5909
                      std::ostream* os) {
5910
    *os << "pattern: '" << component.pattern
5911
        << "', has_regexp_groups: " << component.has_regexp_groups
5912
        << "group_name_list: ";
5913
    for (const auto& name : component.group_name_list) {
5914
      *os << name << ", ";
5915
    }
5916
  }
5917
#endif  // ADA_TESTING
5918
5919
  typename regex_provider::regex_type regexp{};
5920
  std::string pattern{};
5921
  std::vector<std::string> group_name_list{};
5922
  // For EXACT_MATCH type: the literal string to compare against
5923
  std::string exact_match_value{};
5924
  bool has_regexp_groups = false;
5925
  // Cached pattern type for fast-path optimization
5926
  url_pattern_component_type type = url_pattern_component_type::REGEXP;
5927
};
5928
5929
// A URLPattern input can be either a string or a URLPatternInit object.
5930
// If it is a string, it must be a valid UTF-8 string.
5931
using url_pattern_input = std::variant<std::string_view, url_pattern_init>;
5932
5933
// A struct providing the URLPattern matching results for all
5934
// components of a URL. The URLPatternResult API is defined as
5935
// part of the URLPattern specification.
5936
struct url_pattern_result {
5937
  std::vector<url_pattern_input> inputs;
5938
  url_pattern_component_result protocol;
5939
  url_pattern_component_result username;
5940
  url_pattern_component_result password;
5941
  url_pattern_component_result hostname;
5942
  url_pattern_component_result port;
5943
  url_pattern_component_result pathname;
5944
  url_pattern_component_result search;
5945
  url_pattern_component_result hash;
5946
};
5947
5948
struct url_pattern_options {
5949
  bool ignore_case = false;
5950
5951
#if ADA_TESTING
5952
  friend void PrintTo(const url_pattern_options& options, std::ostream* os) {
5953
    *os << "ignore_case: '" << options.ignore_case;
5954
  }
5955
#endif  // ADA_TESTING
5956
};
5957
5958
/**
5959
 * @brief URL pattern matching class implementing the URLPattern API.
5960
 *
5961
 * URLPattern provides a way to match URLs against patterns with wildcards
5962
 * and named capture groups. It's useful for routing, URL-based dispatching,
5963
 * and URL validation.
5964
 *
5965
 * Pattern syntax supports:
5966
 * - Literal text matching
5967
 * - Named groups: `:name` (matches up to the next separator)
5968
 * - Wildcards: `*` (matches everything)
5969
 * - Custom regex: `(pattern)`
5970
 * - Optional segments: `:name?`
5971
 * - Repeated segments: `:name+`, `:name*`
5972
 *
5973
 * @tparam regex_provider The regex implementation to use for pattern matching.
5974
 *         Must satisfy the url_pattern_regex::regex_concept.
5975
 *
5976
 * @note All string inputs must be valid UTF-8.
5977
 *
5978
 * @see https://urlpattern.spec.whatwg.org/
5979
 */
5980
template <url_pattern_regex::regex_concept regex_provider>
5981
class url_pattern {
5982
 public:
5983
  url_pattern() = default;
5984
5985
  /**
5986
   * If non-null, base_url must pointer at a valid UTF-8 string.
5987
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec
5988
   */
5989
  result<std::optional<url_pattern_result>> exec(
5990
      const url_pattern_input& input,
5991
      const std::string_view* base_url = nullptr);
5992
5993
  /**
5994
   * If non-null, base_url must pointer at a valid UTF-8 string.
5995
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
5996
   */
5997
  result<bool> test(const url_pattern_input& input,
5998
                    const std::string_view* base_url = nullptr);
5999
6000
  /**
6001
   * @see https://urlpattern.spec.whatwg.org/#url-pattern-match
6002
   * This function expects a valid UTF-8 string if input is a string.
6003
   */
6004
  result<std::optional<url_pattern_result>> match(
6005
      const url_pattern_input& input,
6006
      const std::string_view* base_url_string = nullptr);
6007
6008
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol
6009
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
6010
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username
6011
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
6012
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password
6013
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
6014
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname
6015
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
6016
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port
6017
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
6018
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname
6019
  [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound;
6020
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search
6021
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
6022
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash
6023
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
6024
6025
  // If ignoreCase is true, the JavaScript regular expression created for each
6026
  // pattern must use the `vi` flag. Otherwise, they must use the `v` flag.
6027
  [[nodiscard]] bool ignore_case() const;
6028
6029
  // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups
6030
  [[nodiscard]] bool has_regexp_groups() const;
6031
6032
  // Helper to test all components at once. Returns true if all match.
6033
  [[nodiscard]] bool test_components(
6034
      std::string_view protocol, std::string_view username,
6035
      std::string_view password, std::string_view hostname,
6036
      std::string_view port, std::string_view pathname, std::string_view search,
6037
      std::string_view hash) const;
6038
6039
#if ADA_TESTING
6040
  friend void PrintTo(const url_pattern& c, std::ostream* os) {
6041
    *os << "protocol_component: '" << c.get_protocol() << ", ";
6042
    *os << "username_component: '" << c.get_username() << ", ";
6043
    *os << "password_component: '" << c.get_password() << ", ";
6044
    *os << "hostname_component: '" << c.get_hostname() << ", ";
6045
    *os << "port_component: '" << c.get_port() << ", ";
6046
    *os << "pathname_component: '" << c.get_pathname() << ", ";
6047
    *os << "search_component: '" << c.get_search() << ", ";
6048
    *os << "hash_component: '" << c.get_hash();
6049
  }
6050
#endif  // ADA_TESTING
6051
6052
  template <url_pattern_regex::regex_concept P>
6053
  friend tl::expected<url_pattern<P>, errors> parser::parse_url_pattern_impl(
6054
      std::variant<std::string_view, url_pattern_init>&& input,
6055
      const std::string_view* base_url, const url_pattern_options* options);
6056
6057
  /**
6058
   * @private
6059
   * We can not make this private due to a LLVM bug.
6060
   * Ref: https://github.com/ada-url/ada/pull/859
6061
   */
6062
  url_pattern_component<regex_provider> protocol_component{};
6063
  /**
6064
   * @private
6065
   * We can not make this private due to a LLVM bug.
6066
   * Ref: https://github.com/ada-url/ada/pull/859
6067
   */
6068
  url_pattern_component<regex_provider> username_component{};
6069
  /**
6070
   * @private
6071
   * We can not make this private due to a LLVM bug.
6072
   * Ref: https://github.com/ada-url/ada/pull/859
6073
   */
6074
  url_pattern_component<regex_provider> password_component{};
6075
  /**
6076
   * @private
6077
   * We can not make this private due to a LLVM bug.
6078
   * Ref: https://github.com/ada-url/ada/pull/859
6079
   */
6080
  url_pattern_component<regex_provider> hostname_component{};
6081
  /**
6082
   * @private
6083
   * We can not make this private due to a LLVM bug.
6084
   * Ref: https://github.com/ada-url/ada/pull/859
6085
   */
6086
  url_pattern_component<regex_provider> port_component{};
6087
  /**
6088
   * @private
6089
   * We can not make this private due to a LLVM bug.
6090
   * Ref: https://github.com/ada-url/ada/pull/859
6091
   */
6092
  url_pattern_component<regex_provider> pathname_component{};
6093
  /**
6094
   * @private
6095
   * We can not make this private due to a LLVM bug.
6096
   * Ref: https://github.com/ada-url/ada/pull/859
6097
   */
6098
  url_pattern_component<regex_provider> search_component{};
6099
  /**
6100
   * @private
6101
   * We can not make this private due to a LLVM bug.
6102
   * Ref: https://github.com/ada-url/ada/pull/859
6103
   */
6104
  url_pattern_component<regex_provider> hash_component{};
6105
  /**
6106
   * @private
6107
   * We can not make this private due to a LLVM bug.
6108
   * Ref: https://github.com/ada-url/ada/pull/859
6109
   */
6110
  bool ignore_case_ = false;
6111
};
6112
}  // namespace ada
6113
#endif  // ADA_INCLUDE_URL_PATTERN
6114
#endif
6115
/* end file include/ada/url_pattern.h */
6116
/* begin file include/ada/url_pattern_helpers.h */
6117
/**
6118
 * @file url_pattern_helpers.h
6119
 * @brief Declaration for the URLPattern helpers.
6120
 */
6121
#ifndef ADA_URL_PATTERN_HELPERS_H
6122
#define ADA_URL_PATTERN_HELPERS_H
6123
6124
6125
#include <string>
6126
#include <tuple>
6127
#include <vector>
6128
6129
#if ADA_INCLUDE_URL_PATTERN
6130
namespace ada {
6131
enum class errors : uint8_t;
6132
}
6133
6134
namespace ada::url_pattern_helpers {
6135
6136
// @see https://urlpattern.spec.whatwg.org/#token
6137
enum class token_type : uint8_t {
6138
  INVALID_CHAR,    // 0
6139
  OPEN,            // 1
6140
  CLOSE,           // 2
6141
  REGEXP,          // 3
6142
  NAME,            // 4
6143
  CHAR,            // 5
6144
  ESCAPED_CHAR,    // 6
6145
  OTHER_MODIFIER,  // 7
6146
  ASTERISK,        // 8
6147
  END,             // 9
6148
};
6149
6150
#ifdef ADA_TESTING
6151
std::string to_string(token_type type);
6152
#endif  // ADA_TESTING
6153
6154
// @see https://urlpattern.spec.whatwg.org/#tokenize-policy
6155
enum class token_policy {
6156
  strict,
6157
  lenient,
6158
};
6159
6160
// @see https://urlpattern.spec.whatwg.org/#tokens
6161
class token {
6162
 public:
6163
  token(token_type _type, size_t _index, std::string_view _value)
6164
0
      : type(_type), index(_index), value(_value) {}
6165
6166
  // A token has an associated type, a string, initially "invalid-char".
6167
  token_type type = token_type::INVALID_CHAR;
6168
6169
  // A token has an associated index, a number, initially 0. It is the position
6170
  // of the first code point in the pattern string represented by the token.
6171
  size_t index = 0;
6172
6173
  // A token has an associated value, a string, initially the empty string. It
6174
  // contains the code points from the pattern string represented by the token.
6175
  std::string_view value{};
6176
};
6177
6178
// @see https://urlpattern.spec.whatwg.org/#pattern-parser
6179
template <url_pattern_encoding_callback F>
6180
class url_pattern_parser {
6181
 public:
6182
  url_pattern_parser(F& encoding_callback_,
6183
                     std::string_view segment_wildcard_regexp_)
6184
0
      : encoding_callback(encoding_callback_),
6185
0
        segment_wildcard_regexp(segment_wildcard_regexp_) {}
6186
6187
0
  bool can_continue() const { return index < tokens.size(); }
6188
6189
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token
6190
  token* try_consume_token(token_type type);
6191
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token
6192
  token* try_consume_modifier_token();
6193
  // @see
6194
  // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token
6195
  token* try_consume_regexp_or_wildcard_token(const token* name_token);
6196
  // @see https://urlpattern.spec.whatwg.org/#consume-text
6197
  std::string consume_text();
6198
  // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token
6199
  bool consume_required_token(token_type type);
6200
  // @see
6201
  // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value
6202
  std::optional<errors> maybe_add_part_from_the_pending_fixed_value()
6203
      ada_warn_unused;
6204
  // @see https://urlpattern.spec.whatwg.org/#add-a-part
6205
  std::optional<errors> add_part(std::string_view prefix, token* name_token,
6206
                                 token* regexp_or_wildcard_token,
6207
                                 std::string_view suyffix,
6208
                                 token* modifier_token) ada_warn_unused;
6209
6210
  std::vector<token> tokens{};
6211
  F& encoding_callback;
6212
  std::string segment_wildcard_regexp;
6213
  std::vector<url_pattern_part> parts{};
6214
  std::string pending_fixed_value{};
6215
  size_t index = 0;
6216
  size_t next_numeric_name = 0;
6217
};
6218
6219
// @see https://urlpattern.spec.whatwg.org/#tokenizer
6220
class Tokenizer {
6221
 public:
6222
  explicit Tokenizer(std::string_view new_input, token_policy new_policy)
6223
0
      : input(new_input), policy(new_policy) {}
6224
6225
  // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point
6226
  constexpr void get_next_code_point();
6227
6228
  // True when the most recent decoded unit was malformed UTF-8.
6229
0
  bool had_invalid_code_point() const { return invalid_code_point; }
6230
6231
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
6232
  constexpr void seek_and_get_next_code_point(size_t index);
6233
6234
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
6235
6236
  void add_token(token_type type, size_t next_position, size_t value_position,
6237
                 size_t value_length);
6238
6239
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
6240
  void add_token_with_default_length(token_type type, size_t next_position,
6241
                                     size_t value_position);
6242
6243
  // @see
6244
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
6245
  void add_token_with_defaults(token_type type);
6246
6247
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
6248
  std::optional<errors> process_tokenizing_error(
6249
      size_t next_position, size_t value_position) ada_warn_unused;
6250
6251
  friend tl::expected<std::vector<token>, errors> tokenize(
6252
      std::string_view input, token_policy policy);
6253
6254
 private:
6255
  // has an associated input, a pattern string, initially the empty string.
6256
  std::string_view input;
6257
  // has an associated policy, a tokenize policy, initially "strict".
6258
  token_policy policy;
6259
  // has an associated token list, a token list, initially an empty list.
6260
  std::vector<token> token_list{};
6261
  // has an associated index, a number, initially 0.
6262
  size_t index = 0;
6263
  // has an associated next index, a number, initially 0.
6264
  size_t next_index = 0;
6265
  // has an associated code point, a Unicode code point, initially null.
6266
  char32_t code_point{};
6267
  // Tracks whether the last decoded code point was malformed UTF-8.
6268
  bool invalid_code_point = false;
6269
};
6270
6271
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
6272
template <url_pattern_regex::regex_concept regex_provider>
6273
struct constructor_string_parser {
6274
  explicit constructor_string_parser(std::string_view new_input,
6275
                                     std::vector<token>&& new_token_list)
6276
      : input(new_input), token_list(std::move(new_token_list)) {}
6277
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
6278
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
6279
6280
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
6281
  enum class State {
6282
    INIT,
6283
    PROTOCOL,
6284
    AUTHORITY,
6285
    USERNAME,
6286
    PASSWORD,
6287
    HOSTNAME,
6288
    PORT,
6289
    PATHNAME,
6290
    SEARCH,
6291
    HASH,
6292
    DONE,
6293
  };
6294
6295
  // @see
6296
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6297
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6298
6299
 private:
6300
  // @see https://urlpattern.spec.whatwg.org/#rewind
6301
  constexpr void rewind();
6302
6303
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6304
  constexpr bool is_hash_prefix();
6305
6306
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6307
  constexpr bool is_search_prefix();
6308
6309
  // @see https://urlpattern.spec.whatwg.org/#change-state
6310
  void change_state(State state, size_t skip);
6311
6312
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6313
  constexpr bool is_group_open() const;
6314
6315
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6316
  constexpr bool is_group_close() const;
6317
6318
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6319
  constexpr bool is_protocol_suffix() const;
6320
6321
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6322
  constexpr bool next_is_authority_slashes() const;
6323
6324
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6325
  constexpr bool is_an_identity_terminator() const;
6326
6327
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6328
  constexpr bool is_pathname_start() const;
6329
6330
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6331
  constexpr bool is_password_prefix() const;
6332
6333
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6334
  constexpr bool is_an_ipv6_open() const;
6335
6336
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6337
  constexpr bool is_an_ipv6_close() const;
6338
6339
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6340
  constexpr bool is_port_prefix() const;
6341
6342
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6343
  constexpr bool is_non_special_pattern_char(size_t index,
6344
                                             uint32_t value) const;
6345
6346
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6347
  constexpr const token* get_safe_token(size_t index) const;
6348
6349
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6350
  std::string make_component_string();
6351
  // has an associated input, a string, which must be set upon creation.
6352
  std::string_view input;
6353
  // has an associated token list, a token list, which must be set upon
6354
  // creation.
6355
  std::vector<token> token_list;
6356
  // has an associated result, a URLPatternInit, initially set to a new
6357
  // URLPatternInit.
6358
  url_pattern_init result{};
6359
  // has an associated component start, a number, initially set to 0.
6360
  size_t component_start = 0;
6361
  // has an associated token index, a number, initially set to 0.
6362
  size_t token_index = 0;
6363
  // has an associated token increment, a number, initially set to 1.
6364
  size_t token_increment = 1;
6365
  // has an associated group depth, a number, initially set to 0.
6366
  size_t group_depth = 0;
6367
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6368
  // 0.
6369
  size_t hostname_ipv6_bracket_depth = 0;
6370
  // has an associated protocol matches a special scheme flag, a boolean,
6371
  // initially set to false.
6372
  bool protocol_matches_a_special_scheme_flag = false;
6373
  // has an associated state, a string, initially set to "init".
6374
  State state = State::INIT;
6375
};
6376
6377
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6378
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6379
6380
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6381
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6382
6383
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6384
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6385
6386
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6387
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6388
6389
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6390
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6391
    std::string_view input);
6392
6393
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6394
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6395
6396
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6397
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6398
    std::string_view input, std::string_view protocol);
6399
6400
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6401
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6402
6403
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6404
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6405
    std::string_view input);
6406
6407
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6408
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6409
6410
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6411
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6412
6413
// @see https://urlpattern.spec.whatwg.org/#tokenize
6414
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6415
                                                  token_policy policy);
6416
6417
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6418
std::string process_base_url_string(std::string_view input,
6419
                                    url_pattern_init::process_type type);
6420
6421
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6422
std::string escape_pattern_string(std::string_view input);
6423
6424
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6425
std::string escape_regexp_string(std::string_view input);
6426
6427
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6428
constexpr bool is_absolute_pathname(
6429
    std::string_view input, url_pattern_init::process_type type) noexcept;
6430
6431
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6432
template <url_pattern_encoding_callback F>
6433
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6434
    std::string_view input, url_pattern_compile_component_options& options,
6435
    F& encoding_callback);
6436
6437
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6438
std::string generate_pattern_string(
6439
    std::vector<url_pattern_part>& part_list,
6440
    url_pattern_compile_component_options& options);
6441
6442
// @see
6443
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6444
std::tuple<std::string, std::vector<std::string>>
6445
generate_regular_expression_and_name_list(
6446
    const std::vector<url_pattern_part>& part_list,
6447
    url_pattern_compile_component_options options);
6448
6449
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6450
bool is_ipv6_address(std::string_view input) noexcept;
6451
6452
// @see
6453
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6454
template <url_pattern_regex::regex_concept regex_provider>
6455
bool protocol_component_matches_special_scheme(
6456
    ada::url_pattern_component<regex_provider>& input);
6457
6458
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6459
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6460
6461
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6462
std::string generate_segment_wildcard_regexp(
6463
    url_pattern_compile_component_options options);
6464
6465
}  // namespace ada::url_pattern_helpers
6466
#endif  // ADA_INCLUDE_URL_PATTERN
6467
#endif
6468
/* end file include/ada/url_pattern_helpers.h */
6469
6470
#include <string>
6471
#include <string_view>
6472
#include <variant>
6473
6474
namespace ada::parser {
6475
#if ADA_INCLUDE_URL_PATTERN
6476
template <url_pattern_regex::regex_concept regex_provider>
6477
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6478
    std::variant<std::string_view, url_pattern_init>&& input,
6479
    const std::string_view* base_url, const url_pattern_options* options) {
6480
  // Let init be null.
6481
  url_pattern_init init;
6482
6483
  // If input is a scalar value string then:
6484
  if (std::holds_alternative<std::string_view>(input)) {
6485
    // Set init to the result of running parse a constructor string given input.
6486
    auto parse_result =
6487
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6488
            std::get<std::string_view>(input));
6489
    if (!parse_result) {
6490
      ada_log("constructor_string_parser::parse failed");
6491
      return tl::unexpected(parse_result.error());
6492
    }
6493
    init = std::move(*parse_result);
6494
    // If baseURL is null and init["protocol"] does not exist, then throw a
6495
    // TypeError.
6496
    if (!base_url && !init.protocol) {
6497
      ada_log("base url is null and protocol is not set");
6498
      return tl::unexpected(errors::type_error);
6499
    }
6500
6501
    // If baseURL is not null, set init["baseURL"] to baseURL.
6502
    if (base_url) {
6503
      init.base_url = std::string(*base_url);
6504
    }
6505
  } else {
6506
    // Assert: input is a URLPatternInit.
6507
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6508
    // If baseURL is not null, then throw a TypeError.
6509
    if (base_url) {
6510
      ada_log("base url is not null");
6511
      return tl::unexpected(errors::type_error);
6512
    }
6513
    // Optimization: Avoid copy by moving the input value.
6514
    // Set init to input.
6515
    init = std::move(std::get<url_pattern_init>(input));
6516
  }
6517
6518
  // Let processedInit be the result of process a URLPatternInit given init,
6519
  // "pattern", null, null, null, null, null, null, null, and null.
6520
  auto processed_init =
6521
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6522
  if (!processed_init) {
6523
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6524
    return tl::unexpected(processed_init.error());
6525
  }
6526
6527
  // For each componentName of  "protocol", "username", "password", "hostname",
6528
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6529
  // not exist, then set processedInit[componentName] to "*".
6530
  ADA_ASSERT_TRUE(processed_init.has_value());
6531
  if (!processed_init->protocol) processed_init->protocol = "*";
6532
  if (!processed_init->username) processed_init->username = "*";
6533
  if (!processed_init->password) processed_init->password = "*";
6534
  if (!processed_init->hostname) processed_init->hostname = "*";
6535
  if (!processed_init->port) processed_init->port = "*";
6536
  if (!processed_init->pathname) processed_init->pathname = "*";
6537
  if (!processed_init->search) processed_init->search = "*";
6538
  if (!processed_init->hash) processed_init->hash = "*";
6539
6540
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6541
  ada_log("-- processed_init->username: ", processed_init->username.value());
6542
  ada_log("-- processed_init->password: ", processed_init->password.value());
6543
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6544
  ada_log("-- processed_init->port: ", processed_init->port.value());
6545
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6546
  ada_log("-- processed_init->search: ", processed_init->search.value());
6547
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6548
6549
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6550
  // is a string which represents its corresponding default port in radix-10
6551
  // using ASCII digits then set processedInit["port"] to the empty string.
6552
  // TODO: Optimization opportunity.
6553
  if (scheme::is_special(*processed_init->protocol)) {
6554
    // file is the only special scheme with no default port; get_special_port
6555
    // returns 0 as its sentinel, so a literal "0" port must not be mistaken for
6556
    // a default and dropped.
6557
    uint16_t default_port = scheme::get_special_port(*processed_init->protocol);
6558
    if (default_port != 0 &&
6559
        std::to_string(default_port) == processed_init->port.value()) {
6560
      processed_init->port->clear();
6561
    }
6562
  }
6563
6564
  // Let urlPattern be a new URL pattern.
6565
  url_pattern<regex_provider> url_pattern_{};
6566
6567
  // Set urlPattern's protocol component to the result of compiling a component
6568
  // given processedInit["protocol"], canonicalize a protocol, and default
6569
  // options.
6570
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6571
      processed_init->protocol.value(),
6572
      url_pattern_helpers::canonicalize_protocol,
6573
      url_pattern_compile_component_options::DEFAULT);
6574
  if (!protocol_component) {
6575
    ada_log("url_pattern_component::compile failed for protocol ",
6576
            processed_init->protocol.value());
6577
    return tl::unexpected(protocol_component.error());
6578
  }
6579
  url_pattern_.protocol_component = std::move(*protocol_component);
6580
6581
  // Set urlPattern's username component to the result of compiling a component
6582
  // given processedInit["username"], canonicalize a username, and default
6583
  // options.
6584
  auto username_component = url_pattern_component<regex_provider>::compile(
6585
      processed_init->username.value(),
6586
      url_pattern_helpers::canonicalize_username,
6587
      url_pattern_compile_component_options::DEFAULT);
6588
  if (!username_component) {
6589
    ada_log("url_pattern_component::compile failed for username ",
6590
            processed_init->username.value());
6591
    return tl::unexpected(username_component.error());
6592
  }
6593
  url_pattern_.username_component = std::move(*username_component);
6594
6595
  // Set urlPattern's password component to the result of compiling a component
6596
  // given processedInit["password"], canonicalize a password, and default
6597
  // options.
6598
  auto password_component = url_pattern_component<regex_provider>::compile(
6599
      processed_init->password.value(),
6600
      url_pattern_helpers::canonicalize_password,
6601
      url_pattern_compile_component_options::DEFAULT);
6602
  if (!password_component) {
6603
    ada_log("url_pattern_component::compile failed for password ",
6604
            processed_init->password.value());
6605
    return tl::unexpected(password_component.error());
6606
  }
6607
  url_pattern_.password_component = std::move(*password_component);
6608
6609
  // TODO: Optimization opportunity. The following if statement can be
6610
  // simplified.
6611
  // If the result running hostname pattern is an IPv6 address given
6612
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6613
  // to the result of compiling a component given processedInit["hostname"],
6614
  // canonicalize an IPv6 hostname, and hostname options.
6615
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6616
    ada_log("processed_init->hostname is ipv6 address");
6617
    // then set urlPattern's hostname component to the result of compiling a
6618
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6619
    // and hostname options.
6620
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6621
        processed_init->hostname.value(),
6622
        url_pattern_helpers::canonicalize_ipv6_hostname,
6623
        url_pattern_compile_component_options::DEFAULT);
6624
    if (!hostname_component) {
6625
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6626
              processed_init->hostname.value());
6627
      return tl::unexpected(hostname_component.error());
6628
    }
6629
    url_pattern_.hostname_component = std::move(*hostname_component);
6630
  } else {
6631
    // Otherwise, set urlPattern's hostname component to the result of compiling
6632
    // a component given processedInit["hostname"], canonicalize a hostname, and
6633
    // hostname options.
6634
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6635
        processed_init->hostname.value(),
6636
        url_pattern_helpers::canonicalize_hostname,
6637
        url_pattern_compile_component_options::HOSTNAME);
6638
    if (!hostname_component) {
6639
      ada_log("url_pattern_component::compile failed for hostname ",
6640
              processed_init->hostname.value());
6641
      return tl::unexpected(hostname_component.error());
6642
    }
6643
    url_pattern_.hostname_component = std::move(*hostname_component);
6644
  }
6645
6646
  // Set urlPattern's port component to the result of compiling a component
6647
  // given processedInit["port"], canonicalize a port, and default options.
6648
  auto port_component = url_pattern_component<regex_provider>::compile(
6649
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6650
      url_pattern_compile_component_options::DEFAULT);
6651
  if (!port_component) {
6652
    ada_log("url_pattern_component::compile failed for port ",
6653
            processed_init->port.value());
6654
    return tl::unexpected(port_component.error());
6655
  }
6656
  url_pattern_.port_component = std::move(*port_component);
6657
6658
  // Let compileOptions be a copy of the default options with the ignore case
6659
  // property set to options["ignoreCase"].
6660
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6661
  if (options) {
6662
    compile_options.ignore_case = options->ignore_case;
6663
    // Recorded on the pattern so that url_pattern::ignore_case() reports the
6664
    // option it was created with.
6665
    url_pattern_.ignore_case_ = options->ignore_case;
6666
  }
6667
6668
  // TODO: Optimization opportunity: Simplify this if statement.
6669
  // If the result of running protocol component matches a special scheme given
6670
  // urlPattern's protocol component is true, then:
6671
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6672
          regex_provider>(url_pattern_.protocol_component)) {
6673
    // Let pathCompileOptions be copy of the pathname options with the ignore
6674
    // case property set to options["ignoreCase"].
6675
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6676
    if (options) {
6677
      path_compile_options.ignore_case = options->ignore_case;
6678
    }
6679
6680
    // Set urlPattern's pathname component to the result of compiling a
6681
    // component given processedInit["pathname"], canonicalize a pathname, and
6682
    // pathCompileOptions.
6683
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6684
        processed_init->pathname.value(),
6685
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6686
    if (!pathname_component) {
6687
      ada_log("url_pattern_component::compile failed for pathname ",
6688
              processed_init->pathname.value());
6689
      return tl::unexpected(pathname_component.error());
6690
    }
6691
    url_pattern_.pathname_component = std::move(*pathname_component);
6692
  } else {
6693
    // Otherwise set urlPattern's pathname component to the result of compiling
6694
    // a component given processedInit["pathname"], canonicalize an opaque
6695
    // pathname, and compileOptions.
6696
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6697
        processed_init->pathname.value(),
6698
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6699
    if (!pathname_component) {
6700
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6701
              processed_init->pathname.value());
6702
      return tl::unexpected(pathname_component.error());
6703
    }
6704
    url_pattern_.pathname_component = std::move(*pathname_component);
6705
  }
6706
6707
  // Set urlPattern's search component to the result of compiling a component
6708
  // given processedInit["search"], canonicalize a search, and compileOptions.
6709
  auto search_component = url_pattern_component<regex_provider>::compile(
6710
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6711
      compile_options);
6712
  if (!search_component) {
6713
    ada_log("url_pattern_component::compile failed for search ",
6714
            processed_init->search.value());
6715
    return tl::unexpected(search_component.error());
6716
  }
6717
  url_pattern_.search_component = std::move(*search_component);
6718
6719
  // Set urlPattern's hash component to the result of compiling a component
6720
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6721
  auto hash_component = url_pattern_component<regex_provider>::compile(
6722
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6723
      compile_options);
6724
  if (!hash_component) {
6725
    ada_log("url_pattern_component::compile failed for hash ",
6726
            processed_init->hash.value());
6727
    return tl::unexpected(hash_component.error());
6728
  }
6729
  url_pattern_.hash_component = std::move(*hash_component);
6730
6731
  // Return urlPattern.
6732
  return url_pattern_;
6733
}
6734
#endif  // ADA_INCLUDE_URL_PATTERN
6735
6736
}  // namespace ada::parser
6737
6738
#endif  // ADA_PARSER_INL_H
6739
/* end file include/ada/parser-inl.h */
6740
/* begin file include/ada/scheme-inl.h */
6741
/**
6742
 * @file scheme-inl.h
6743
 * @brief Definitions for the URL scheme.
6744
 */
6745
#ifndef ADA_SCHEME_INL_H
6746
#define ADA_SCHEME_INL_H
6747
6748
6749
namespace ada::scheme {
6750
6751
/**
6752
 * @namespace ada::scheme::details
6753
 * @brief Includes the definitions for scheme specific entities
6754
 */
6755
namespace details {
6756
// for use with is_special and get_special_port
6757
// Spaces, if present, are removed from URL.
6758
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6759
                                                "ftp",  "wss", "file",  " "};
6760
// for use with get_special_port
6761
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6762
6763
// @private
6764
// convert a string_view to a 64-bit integer key for fast comparison
6765
0
constexpr uint64_t make_key(std::string_view sv) {
6766
0
  uint64_t val = 0;
6767
0
  for (size_t i = 0; i < sv.size(); i++)
6768
0
    val |= (uint64_t)(uint8_t)sv[i] << (i * 8);
6769
0
  return val;
6770
0
}
6771
// precomputed keys for the special schemes, indexed by a hash of the input
6772
// string
6773
constexpr uint64_t scheme_keys[] = {
6774
    make_key("http"),   // 0: HTTP
6775
    0,                  // 1: sentinel
6776
    make_key("https"),  // 2: HTTPS
6777
    make_key("ws"),     // 3: WS
6778
    make_key("ftp"),    // 4: FTP
6779
    make_key("wss"),    // 5: WSS
6780
    make_key("file"),   // 6: FILE
6781
    0,                  // 7: sentinel
6782
};
6783
6784
// @private
6785
// branchless load of up to 5 characters into a uint64_t, padding with zeros if
6786
// n < 5
6787
131k
inline uint64_t branchless_load5(const char* p, size_t n) {
6788
131k
  uint64_t input = (uint8_t)p[0];
6789
131k
  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
6790
131k
  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
6791
131k
  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
6792
131k
  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
6793
131k
  return input;
6794
131k
}
6795
}  // namespace details
6796
6797
/****
6798
 * @private
6799
 * In is_special, get_scheme_type, and get_special_port, we
6800
 * use a standard hashing technique to find the index of the scheme in
6801
 * the is_special_list. The hashing technique is based on the size of
6802
 * the scheme and the first character of the scheme. It ensures that we
6803
 * do at most one string comparison per call. If the protocol is
6804
 * predictible (e.g., it is always "http"), we can get a better average
6805
 * performance by using a simpler approach where we loop and compare
6806
 * scheme with all possible protocols starting with the most likely
6807
 * protocol. Doing multiple comparisons may have a poor worst case
6808
 * performance, however. In this instance, we choose a potentially
6809
 * slightly lower best-case performance for a better worst-case
6810
 * performance. We can revisit this choice at any time.
6811
 *
6812
 * Reference:
6813
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6814
 * More C++ gems 17 (2000).
6815
 *
6816
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6817
 *
6818
 * Reference: https://github.com/ada-url/ada/issues/617
6819
 ****/
6820
6821
9.36k
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6822
9.36k
  if (scheme.empty()) {
6823
0
    return false;
6824
0
  }
6825
9.36k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6826
9.36k
  const std::string_view target = details::is_special_list[hash_value];
6827
9.36k
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6828
9.36k
}
6829
0
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6830
0
  if (scheme.empty()) {
6831
0
    return 0;
6832
0
  }
6833
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6834
0
  const std::string_view target = details::is_special_list[hash_value];
6835
0
  if (scheme.size() == target.size() &&
6836
0
      details::branchless_load5(scheme.data(), scheme.size()) ==
6837
0
          details::scheme_keys[hash_value]) {
6838
0
    return details::special_ports[hash_value];
6839
0
  } else {
6840
0
    return 0;
6841
0
  }
6842
0
}
6843
70.9k
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6844
70.9k
  return details::special_ports[int(type)];
6845
70.9k
}
6846
183k
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6847
183k
  if (scheme.empty()) {
6848
0
    return ada::scheme::NOT_SPECIAL;
6849
0
  }
6850
183k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6851
183k
  const std::string_view target = details::is_special_list[hash_value];
6852
183k
  if (scheme.size() == target.size() &&
6853
131k
      details::branchless_load5(scheme.data(), scheme.size()) ==
6854
131k
          details::scheme_keys[hash_value]) {
6855
104k
    return ada::scheme::type(hash_value);
6856
104k
  } else {
6857
79.5k
    return ada::scheme::NOT_SPECIAL;
6858
79.5k
  }
6859
183k
}
6860
6861
}  // namespace ada::scheme
6862
6863
#endif  // ADA_SCHEME_INL_H
6864
/* end file include/ada/scheme-inl.h */
6865
/* begin file include/ada/serializers.h */
6866
/**
6867
 * @file serializers.h
6868
 * @brief IP address serialization utilities.
6869
 *
6870
 * This header provides functions for converting IP addresses to their
6871
 * string representations according to the WHATWG URL Standard.
6872
 */
6873
#ifndef ADA_SERIALIZERS_H
6874
#define ADA_SERIALIZERS_H
6875
6876
6877
#include <array>
6878
#include <string>
6879
6880
/**
6881
 * @namespace ada::serializers
6882
 * @brief IP address serialization functions.
6883
 *
6884
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6885
 */
6886
namespace ada::serializers {
6887
6888
/**
6889
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6890
 * Used for :: compression in IPv6 serialization.
6891
 *
6892
 * @param address The 8 16-bit pieces of the IPv6 address.
6893
 * @param[out] compress Index of the start of the longest zero sequence.
6894
 * @param[out] compress_length Length of the longest zero sequence.
6895
 */
6896
void find_longest_sequence_of_ipv6_pieces(
6897
    const std::array<uint16_t, 8>& address, size_t& compress,
6898
    size_t& compress_length) noexcept;
6899
6900
/**
6901
 * Serializes an IPv6 address to its string representation.
6902
 *
6903
 * @param address The 8 16-bit pieces of the IPv6 address.
6904
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6905
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6906
 */
6907
std::string ipv6(const std::array<uint16_t, 8>& address);
6908
6909
/**
6910
 * Serializes an IPv4 address to its dotted-decimal string representation.
6911
 *
6912
 * @param address The 32-bit IPv4 address as an integer.
6913
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6914
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6915
 */
6916
std::string ipv4(uint64_t address);
6917
6918
}  // namespace ada::serializers
6919
6920
#endif  // ADA_SERIALIZERS_H
6921
/* end file include/ada/serializers.h */
6922
/* begin file include/ada/state.h */
6923
/**
6924
 * @file state.h
6925
 * @brief URL parser state machine states.
6926
 *
6927
 * Defines the states used by the URL parsing state machine as specified
6928
 * in the WHATWG URL Standard.
6929
 *
6930
 * @see https://url.spec.whatwg.org/#url-parsing
6931
 */
6932
#ifndef ADA_STATE_H
6933
#define ADA_STATE_H
6934
6935
6936
#include <string>
6937
6938
namespace ada {
6939
6940
/**
6941
 * @brief States in the URL parsing state machine.
6942
 *
6943
 * The URL parser processes input through a sequence of states, each handling
6944
 * a specific part of the URL syntax.
6945
 *
6946
 * @see https://url.spec.whatwg.org/#url-parsing
6947
 */
6948
enum class state {
6949
  /**
6950
   * @see https://url.spec.whatwg.org/#authority-state
6951
   */
6952
  AUTHORITY,
6953
6954
  /**
6955
   * @see https://url.spec.whatwg.org/#scheme-start-state
6956
   */
6957
  SCHEME_START,
6958
6959
  /**
6960
   * @see https://url.spec.whatwg.org/#scheme-state
6961
   */
6962
  SCHEME,
6963
6964
  /**
6965
   * @see https://url.spec.whatwg.org/#host-state
6966
   */
6967
  HOST,
6968
6969
  /**
6970
   * @see https://url.spec.whatwg.org/#no-scheme-state
6971
   */
6972
  NO_SCHEME,
6973
6974
  /**
6975
   * @see https://url.spec.whatwg.org/#fragment-state
6976
   */
6977
  FRAGMENT,
6978
6979
  /**
6980
   * @see https://url.spec.whatwg.org/#relative-state
6981
   */
6982
  RELATIVE_SCHEME,
6983
6984
  /**
6985
   * @see https://url.spec.whatwg.org/#relative-slash-state
6986
   */
6987
  RELATIVE_SLASH,
6988
6989
  /**
6990
   * @see https://url.spec.whatwg.org/#file-state
6991
   */
6992
  FILE,
6993
6994
  /**
6995
   * @see https://url.spec.whatwg.org/#file-host-state
6996
   */
6997
  FILE_HOST,
6998
6999
  /**
7000
   * @see https://url.spec.whatwg.org/#file-slash-state
7001
   */
7002
  FILE_SLASH,
7003
7004
  /**
7005
   * @see https://url.spec.whatwg.org/#path-or-authority-state
7006
   */
7007
  PATH_OR_AUTHORITY,
7008
7009
  /**
7010
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
7011
   */
7012
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
7013
7014
  /**
7015
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
7016
   */
7017
  SPECIAL_AUTHORITY_SLASHES,
7018
7019
  /**
7020
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
7021
   */
7022
  SPECIAL_RELATIVE_OR_AUTHORITY,
7023
7024
  /**
7025
   * @see https://url.spec.whatwg.org/#query-state
7026
   */
7027
  QUERY,
7028
7029
  /**
7030
   * @see https://url.spec.whatwg.org/#path-state
7031
   */
7032
  PATH,
7033
7034
  /**
7035
   * @see https://url.spec.whatwg.org/#path-start-state
7036
   */
7037
  PATH_START,
7038
7039
  /**
7040
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
7041
   */
7042
  OPAQUE_PATH,
7043
7044
  /**
7045
   * @see https://url.spec.whatwg.org/#port-state
7046
   */
7047
  PORT,
7048
};
7049
7050
/**
7051
 * Converts a parser state to its string name for debugging.
7052
 * @param s The state to convert.
7053
 * @return A string representation of the state.
7054
 */
7055
ada_warn_unused std::string to_string(ada::state s);
7056
7057
}  // namespace ada
7058
7059
#endif  // ADA_STATE_H
7060
/* end file include/ada/state.h */
7061
/* begin file include/ada/unicode.h */
7062
/**
7063
 * @file unicode.h
7064
 * @brief Definitions for all unicode specific functions.
7065
 */
7066
#ifndef ADA_UNICODE_H
7067
#define ADA_UNICODE_H
7068
7069
7070
#include <string>
7071
#include <string_view>
7072
#include <optional>
7073
7074
/**
7075
 * Unicode operations. These functions are not part of our public API and may
7076
 * change at any time.
7077
 *
7078
 * @private
7079
 * @namespace ada::unicode
7080
 * @brief Includes the definitions for unicode operations
7081
 */
7082
namespace ada::unicode {
7083
7084
/**
7085
 * @private
7086
 * We receive a UTF-8 string representing a domain name.
7087
 * If the string is percent encoded, we apply percent decoding.
7088
 *
7089
 * Given a domain, we need to identify its labels.
7090
 * They are separated by label-separators:
7091
 *
7092
 * U+002E (.) FULL STOP
7093
 * U+FF0E FULLWIDTH FULL STOP
7094
 * U+3002 IDEOGRAPHIC FULL STOP
7095
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
7096
 *
7097
 * They are all mapped to U+002E.
7098
 *
7099
 * We process each label into a string that should not exceed 63 octets.
7100
 * If the string is already punycode (starts with "xn--"), then we must
7101
 * scan it to look for unallowed code points.
7102
 * Otherwise, if the string is not pure ASCII, we need to transcode it
7103
 * to punycode by following RFC 3454 which requires us to
7104
 * - Map characters  (see section 3),
7105
 * - Normalize (see section 4),
7106
 * - Reject forbidden characters,
7107
 * - Check for right-to-left characters and if so, check all requirements (see
7108
 * section 6),
7109
 * - Optionally reject based on unassigned code points (section 7).
7110
 *
7111
 * The Unicode standard provides a table of code points with a mapping, a list
7112
 * of forbidden code points and so forth. This table is subject to change and
7113
 * will vary based on the implementation. For Unicode 15, the table is at
7114
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
7115
 * If you use ICU, they parse this table and map it to code using a Python
7116
 * script.
7117
 *
7118
 * The resulting strings should not exceed 255 octets according to RFC 1035
7119
 * section 2.3.4. ICU checks for label size and domain size, but these errors
7120
 * are ignored.
7121
 *
7122
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
7123
 *
7124
 */
7125
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
7126
              size_t first_percent);
7127
7128
/**
7129
 * @private
7130
 * Checks if the input has tab or newline characters.
7131
 *
7132
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
7133
 * enough that compilers like GCC can 'autovectorize it'.
7134
 */
7135
ada_really_inline bool has_tabs_or_newline(
7136
    std::string_view user_input) noexcept;
7137
7138
/**
7139
 * @private
7140
 * Checks if the input is a forbidden host code point.
7141
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
7142
 */
7143
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
7144
7145
/**
7146
 * @private
7147
 * Checks if the input contains a forbidden domain code point.
7148
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7149
 */
7150
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
7151
    const char* input, size_t length) noexcept;
7152
7153
/**
7154
 * @private
7155
 * Checks if the input contains a forbidden domain code point in which case
7156
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
7157
 * then the second bit is set to 1.
7158
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7159
 */
7160
ada_really_inline constexpr uint8_t
7161
contains_forbidden_domain_code_point_or_upper(const char* input,
7162
                                              size_t length) noexcept;
7163
7164
/**
7165
 * @private
7166
 * Checks if the input is a forbidden domain code point.
7167
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7168
 */
7169
ada_really_inline constexpr bool is_forbidden_domain_code_point(
7170
    char c) noexcept;
7171
7172
/**
7173
 * @private
7174
 * Checks if the input is alphanumeric, '+', '-' or '.'
7175
 */
7176
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
7177
7178
/**
7179
 * @private
7180
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
7181
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
7182
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
7183
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
7184
 */
7185
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
7186
7187
/**
7188
 * @private
7189
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
7190
 * inclusive.
7191
 */
7192
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
7193
7194
/**
7195
 * @private
7196
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
7197
 * character.
7198
 */
7199
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
7200
7201
/**
7202
 * @private
7203
 * Checks if the input is a C0 control or space character.
7204
 *
7205
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
7206
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
7207
 * SEPARATOR ONE, inclusive.
7208
 */
7209
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
7210
7211
/**
7212
 * @private
7213
 * Checks if the input is a ASCII tab or newline character.
7214
 *
7215
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
7216
 */
7217
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
7218
7219
/**
7220
 * @private
7221
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
7222
 * match for ".%2e", "%2e.", or "%2e%2e".
7223
 */
7224
ada_really_inline constexpr bool is_double_dot_path_segment(
7225
    std::string_view input) noexcept;
7226
7227
/**
7228
 * @private
7229
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
7230
 * match for "%2e".
7231
 */
7232
ada_really_inline constexpr bool is_single_dot_path_segment(
7233
    std::string_view input) noexcept;
7234
7235
/**
7236
 * @private
7237
 * @details ipv4 character might contain 0-9 or a-f character ranges.
7238
 */
7239
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
7240
7241
/**
7242
 * @private
7243
 * @details Convert hex to binary. Caller is responsible to ensure that
7244
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
7245
 */
7246
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
7247
7248
/**
7249
 * @private
7250
 * first_percent should be  = input.find('%')
7251
 *
7252
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
7253
 * @author Node.js
7254
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
7255
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
7256
 */
7257
std::string percent_decode(std::string_view input, size_t first_percent);
7258
7259
/**
7260
 * Decode an application/x-www-form-urlencoded component: map '+' to space,
7261
 * then percent-decode. Single allocation; no intermediate string.
7262
 *
7263
 * @param input A form-urlencoded component (key or value).
7264
 * @return The decoded string.
7265
 * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
7266
 */
7267
std::string form_urlencoded_decode(std::string_view input);
7268
7269
/**
7270
 * @private
7271
 * Returns a percent-encoding string whether percent encoding was needed or not.
7272
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7273
 */
7274
std::string percent_encode(std::string_view input,
7275
                           const uint8_t character_set[]);
7276
/**
7277
 * @private
7278
 * Returns a percent-encoded string version of input, while starting the percent
7279
 * encoding at the provided index.
7280
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7281
 */
7282
std::string percent_encode(std::string_view input,
7283
                           const uint8_t character_set[], size_t index);
7284
/**
7285
 * @private
7286
 * Returns true if percent encoding was needed, in which case, we store
7287
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
7288
 * true, the content is appended to 'out'.
7289
 * If percent encoding is not needed, out is left unchanged.
7290
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7291
 */
7292
template <bool append>
7293
bool percent_encode(std::string_view input, const uint8_t character_set[],
7294
                    std::string& out);
7295
/**
7296
 * @private
7297
 * Returns the index at which percent encoding should start, or (equivalently),
7298
 * the length of the prefix that does not require percent encoding.
7299
 */
7300
ada_really_inline size_t percent_encode_index(std::string_view input,
7301
                                              const uint8_t character_set[]);
7302
/**
7303
 * @private
7304
 * Lowers the string in-place, assuming that the content is ASCII.
7305
 * Return true if the content was ASCII.
7306
 */
7307
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
7308
}  // namespace ada::unicode
7309
7310
#endif  // ADA_UNICODE_H
7311
/* end file include/ada/unicode.h */
7312
/* begin file include/ada/url_base-inl.h */
7313
/**
7314
 * @file url_base-inl.h
7315
 * @brief Inline functions for url base
7316
 */
7317
#ifndef ADA_URL_BASE_INL_H
7318
#define ADA_URL_BASE_INL_H
7319
7320
7321
#include <string>
7322
#if ADA_REGULAR_VISUAL_STUDIO
7323
#include <intrin.h>
7324
#endif  // ADA_REGULAR_VISUAL_STUDIO
7325
7326
namespace ada {
7327
7328
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
7329
1.80M
    const noexcept {
7330
1.80M
  return type != ada::scheme::NOT_SPECIAL;
7331
1.80M
}
7332
7333
14.4k
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
7334
14.4k
  return ada::scheme::get_special_port(type);
7335
14.4k
}
7336
7337
[[nodiscard]] ada_really_inline uint16_t
7338
51.9k
url_base::scheme_default_port() const noexcept {
7339
51.9k
  return scheme::get_special_port(type);
7340
51.9k
}
7341
7342
}  // namespace ada
7343
7344
#endif  // ADA_URL_BASE_INL_H
7345
/* end file include/ada/url_base-inl.h */
7346
/* begin file include/ada/url-inl.h */
7347
/**
7348
 * @file url-inl.h
7349
 * @brief Definitions for the URL
7350
 */
7351
#ifndef ADA_URL_INL_H
7352
#define ADA_URL_INL_H
7353
7354
7355
#include <charconv>
7356
#include <cstring>
7357
#include <optional>
7358
#include <string>
7359
#if ADA_REGULAR_VISUAL_STUDIO
7360
#include <intrin.h>
7361
#endif  // ADA_REGULAR_VISUAL_STUDIO
7362
7363
namespace ada {
7364
393k
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7365
393k
  return !username.empty() || !password.empty();
7366
393k
}
7367
31.4k
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7368
31.4k
  return port.has_value();
7369
31.4k
}
7370
72.4k
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7371
72.4k
  return !host.has_value() || host->empty() || type == ada::scheme::type::FILE;
7372
72.4k
}
7373
31.4k
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7374
31.4k
  if (!host.has_value()) {
7375
3.57k
    return false;
7376
3.57k
  }
7377
27.9k
  return host->empty();
7378
31.4k
}
7379
31.4k
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7380
31.4k
  return host.has_value();
7381
31.4k
}
7382
0
inline std::ostream& operator<<(std::ostream& out, const ada::url& u) {
7383
0
  return out << u.to_string();
7384
0
}
7385
7386
31.4k
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7387
31.4k
  return path.size();
7388
31.4k
}
7389
7390
38.8k
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7391
38.8k
  return path;
7392
38.8k
}
7393
7394
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7395
31.4k
    const {
7396
31.4k
  url_components out{};
7397
7398
  // protocol ends with ':'. for example: "https:"
7399
31.4k
  out.protocol_end = uint32_t(get_protocol().size());
7400
7401
  // Trailing index is always the next character of the current one.
7402
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7403
31.4k
  size_t running_index = out.protocol_end;
7404
7405
31.4k
  if (host.has_value()) {
7406
    // 2 characters for "//" and 1 character for starting index
7407
27.9k
    out.host_start = out.protocol_end + 2;
7408
7409
27.9k
    if (has_credentials()) {
7410
16.0k
      out.username_end = uint32_t(out.host_start + username.size());
7411
7412
16.0k
      out.host_start += uint32_t(username.size());
7413
7414
16.0k
      if (!password.empty()) {
7415
15.6k
        out.host_start += uint32_t(password.size() + 1);
7416
15.6k
      }
7417
7418
      // host_start is the '@' when there are credentials, exactly as
7419
      // url_aggregator reports it, so the host itself begins one byte later.
7420
16.0k
      out.host_end = uint32_t(out.host_start + 1 + host->size());
7421
16.0k
    } else {
7422
11.8k
      out.username_end = out.host_start;
7423
7424
      // Host does not start with "@" if it does not include credentials.
7425
11.8k
      out.host_end = uint32_t(out.host_start + host->size());
7426
11.8k
    }
7427
7428
    // host_end is one past the host, so it is already the next index.
7429
27.9k
    running_index = out.host_end;
7430
27.9k
  } else {
7431
    // Update host start and end date to the same index, since it does not
7432
    // exist.
7433
3.57k
    out.host_start = out.protocol_end;
7434
3.57k
    out.host_end = out.host_start;
7435
7436
3.57k
    if (!has_opaque_path && path.starts_with("//")) {
7437
      // If url's host is null, url does not have an opaque path, url's path's
7438
      // size is greater than 1, and url's path[0] is the empty string, then
7439
      // append U+002F (/) followed by U+002E (.) to output.
7440
48
      running_index = out.protocol_end + 2;
7441
3.53k
    } else {
7442
3.53k
      running_index = out.protocol_end;
7443
3.53k
    }
7444
3.57k
  }
7445
7446
31.4k
  if (port.has_value()) {
7447
2.41k
    out.port = *port;
7448
2.41k
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7449
2.41k
  }
7450
7451
31.4k
  out.pathname_start = uint32_t(running_index);
7452
7453
31.4k
  running_index += path.size();
7454
7455
31.4k
  if (query.has_value()) {
7456
17.4k
    out.search_start = uint32_t(running_index);
7457
17.4k
    running_index += get_search().size();
7458
17.4k
    if (get_search().empty()) {
7459
495
      running_index++;
7460
495
    }
7461
17.4k
  }
7462
7463
31.4k
  if (hash.has_value()) {
7464
17.3k
    out.hash_start = uint32_t(running_index);
7465
17.3k
  }
7466
7467
31.4k
  return out;
7468
31.4k
}
7469
7470
230
inline void url::update_base_hostname(std::string_view input) { host = input; }
7471
7472
23.8k
inline void url::update_unencoded_base_hash(std::string_view input) {
7473
  // We do the percent encoding
7474
23.8k
  hash = unicode::percent_encode(input,
7475
23.8k
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7476
23.8k
}
7477
7478
inline void url::update_base_search(std::string_view input,
7479
24.0k
                                    const uint8_t query_percent_encode_set[]) {
7480
24.0k
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7481
24.0k
}
7482
7483
0
inline void url::update_base_search(std::optional<std::string>&& input) {
7484
0
  query = std::move(input);
7485
0
}
7486
7487
17.2k
inline void url::update_base_pathname(const std::string_view input) {
7488
17.2k
  path = input;
7489
17.2k
}
7490
7491
0
inline void url::update_base_username(const std::string_view input) {
7492
0
  username = input;
7493
0
}
7494
7495
0
inline void url::update_base_password(const std::string_view input) {
7496
0
  password = input;
7497
0
}
7498
7499
0
inline void url::update_base_port(std::optional<uint16_t> input) {
7500
0
  port = input;
7501
0
}
7502
7503
40
constexpr void url::clear_pathname() { path.clear(); }
7504
7505
707
constexpr void url::clear_search() { query = std::nullopt; }
7506
7507
31.4k
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7508
31.4k
  return hash.has_value();
7509
31.4k
}
7510
7511
62.9k
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7512
62.9k
  return query.has_value();
7513
62.9k
}
7514
7515
4.62k
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7516
7517
17.7k
inline void url::set_scheme(std::string&& new_scheme) noexcept {
7518
17.7k
  type = ada::scheme::get_scheme_type(new_scheme);
7519
  // We only move the 'scheme' if it is non-special.
7520
17.7k
  if (!is_special()) {
7521
11.3k
    non_special_scheme = std::move(new_scheme);
7522
11.3k
  }
7523
17.7k
}
7524
7525
0
constexpr void url::copy_scheme(ada::url&& u) {
7526
0
  non_special_scheme = u.non_special_scheme;
7527
0
  type = u.type;
7528
0
}
7529
7530
1.37k
constexpr void url::copy_scheme(const ada::url& u) {
7531
1.37k
  non_special_scheme = u.non_special_scheme;
7532
1.37k
  type = u.type;
7533
1.37k
}
7534
7535
192k
[[nodiscard]] ada_really_inline std::string url::get_href() const {
7536
192k
  if (is_special() && host.has_value() && username.empty() &&
7537
122k
      password.empty() && !port.has_value()) [[likely]] {
7538
114k
    const std::string_view scheme = ada::scheme::details::is_special_list[type];
7539
114k
    const size_t host_size = host->size();
7540
114k
    const size_t path_size = path.size();
7541
114k
    const size_t query_size = query.has_value() ? query->size() : 0;
7542
114k
    const size_t hash_size = hash.has_value() ? hash->size() : 0;
7543
114k
    const size_t total = scheme.size() + 3 + host_size + path_size +
7544
114k
                         (query.has_value() ? query_size + 1 : 0) +
7545
114k
                         (hash.has_value() ? hash_size + 1 : 0);
7546
114k
    std::string output(total, '\0');
7547
114k
    char* p = output.data();
7548
114k
    std::memcpy(p, scheme.data(), scheme.size());
7549
114k
    p += scheme.size();
7550
114k
    p[0] = ':';
7551
114k
    p[1] = '/';
7552
114k
    p[2] = '/';
7553
114k
    p += 3;
7554
    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7555
114k
    std::memcpy(p, host->data(), host_size);
7556
114k
    p += host_size;
7557
114k
    std::memcpy(p, path.data(), path_size);
7558
114k
    p += path_size;
7559
114k
    if (query.has_value()) {
7560
18.5k
      *p++ = '?';
7561
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7562
18.5k
      std::memcpy(p, query->data(), query_size);
7563
18.5k
      p += query_size;
7564
18.5k
    }
7565
114k
    if (hash.has_value()) {
7566
16.8k
      *p++ = '#';
7567
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7568
16.8k
      std::memcpy(p, hash->data(), hash_size);
7569
16.8k
    }
7570
114k
    return output;
7571
114k
  }
7572
7573
77.8k
  std::string output;
7574
77.8k
  output.reserve(get_href_size());
7575
7576
77.8k
  if (is_special()) {
7577
46.0k
    output.append(ada::scheme::details::is_special_list[type]);
7578
46.0k
    output += ':';
7579
46.0k
  } else {
7580
31.8k
    output.append(non_special_scheme);
7581
31.8k
    output += ':';
7582
31.8k
  }
7583
7584
77.8k
  if (host.has_value()) {
7585
50.1k
    output += '/';
7586
50.1k
    output += '/';
7587
50.1k
    if (has_credentials()) {
7588
39.8k
      output.append(username);
7589
39.8k
      if (!password.empty()) {
7590
34.3k
        output += ':';
7591
34.3k
        output.append(password);
7592
34.3k
      }
7593
39.8k
      output += '@';
7594
39.8k
    }
7595
50.1k
    output.append(*host);
7596
50.1k
    if (port.has_value()) {
7597
40.3k
      output += ':';
7598
40.3k
      char port_buf[5];
7599
40.3k
      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
7600
40.3k
      (void)ec;
7601
40.3k
      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
7602
40.3k
    }
7603
50.1k
  } else if (!has_opaque_path && path.starts_with("//")) {
7604
    // If url's host is null, url does not have an opaque path, url's path's
7605
    // size is greater than 1, and url's path[0] is the empty string, then
7606
    // append U+002F (/) followed by U+002E (.) to output.
7607
289
    output += '/';
7608
289
    output += '.';
7609
289
  }
7610
77.8k
  output.append(path);
7611
77.8k
  if (query.has_value()) {
7612
38.9k
    output += '?';
7613
38.9k
    output.append(*query);
7614
38.9k
  }
7615
77.8k
  if (hash.has_value()) {
7616
39.1k
    output += '#';
7617
39.1k
    output.append(*hash);
7618
39.1k
  }
7619
77.8k
  return output;
7620
192k
}
7621
7622
288k
[[nodiscard]] inline size_t url::get_href_size() const noexcept {
7623
288k
  size_t size = 0;
7624
288k
  if (is_special()) {
7625
239k
    size += ada::scheme::details::is_special_list[type].size() + 1;
7626
239k
  } else {
7627
48.3k
    size += non_special_scheme.size() + 1;
7628
48.3k
  }
7629
288k
  if (host.has_value()) {
7630
245k
    size += host->size();
7631
245k
    size += 2;
7632
245k
    if (has_credentials()) {
7633
163k
      size += username.size();
7634
163k
      if (!password.empty()) {
7635
138k
        size += 1 + password.size();
7636
138k
      }
7637
163k
      size += 1;
7638
163k
    }
7639
245k
    if (port.has_value()) {
7640
78.0k
      size += 1;
7641
78.0k
      uint16_t p = *port;
7642
78.0k
      size += (p >= 10000)  ? 5
7643
78.0k
              : (p >= 1000) ? 4
7644
77.2k
              : (p >= 100)  ? 3
7645
12.9k
              : (p >= 10)   ? 2
7646
12.0k
                            : 1;
7647
78.0k
    }
7648
245k
  } else if (!has_opaque_path && path.starts_with("//")) {
7649
444
    size += 2;
7650
444
  }
7651
288k
  size += path.size();
7652
288k
  if (query.has_value()) {
7653
113k
    size += 1 + query->size();
7654
113k
  }
7655
288k
  if (hash.has_value()) {
7656
96.5k
    size += 1 + hash->size();
7657
96.5k
  }
7658
288k
  return size;
7659
288k
}
7660
7661
ada_really_inline size_t url::parse_port(std::string_view view,
7662
25.6k
                                         bool check_trailing_content) noexcept {
7663
25.6k
  ada_log("parse_port('", view, "') ", view.size());
7664
25.6k
  if (!view.empty() && view[0] == '-') {
7665
13
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7666
13
    is_valid = false;
7667
13
    return 0;
7668
13
  }
7669
25.6k
  uint16_t parsed_port{};
7670
25.6k
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7671
25.6k
  if (r.ec == std::errc::result_out_of_range) {
7672
289
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7673
289
    is_valid = false;
7674
289
    return 0;
7675
289
  }
7676
25.3k
  ada_log("parse_port: ", parsed_port);
7677
25.3k
  const auto consumed = size_t(r.ptr - view.data());
7678
25.3k
  ada_log("parse_port: consumed ", consumed);
7679
25.3k
  if (check_trailing_content) {
7680
22.8k
    is_valid &=
7681
22.8k
        (consumed == view.size() || view[consumed] == '/' ||
7682
464
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7683
22.8k
  }
7684
25.3k
  ada_log("parse_port: is_valid = ", is_valid);
7685
25.3k
  if (is_valid) {
7686
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7687
25.0k
    auto default_port = scheme_default_port();
7688
25.0k
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7689
25.0k
                         (default_port != parsed_port);
7690
25.0k
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7691
25.0k
                                                  : std::nullopt;
7692
25.0k
  }
7693
25.3k
  return consumed;
7694
25.6k
}
7695
7696
}  // namespace ada
7697
7698
#endif  // ADA_URL_H
7699
/* end file include/ada/url-inl.h */
7700
/* begin file include/ada/url_components-inl.h */
7701
/**
7702
 * @file url_components.h
7703
 * @brief Declaration for the URL Components
7704
 */
7705
#ifndef ADA_URL_COMPONENTS_INL_H
7706
#define ADA_URL_COMPONENTS_INL_H
7707
7708
7709
namespace ada {
7710
7711
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7712
123k
    const noexcept {
7713
  /**
7714
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7715
   *       |     |    |          | ^^^^|       |   |
7716
   *       |     |    |          | |   |       |   `----- hash_start
7717
   *       |     |    |          | |   |       `--------- search_start
7718
   *       |     |    |          | |   `----------------- pathname_start
7719
   *       |     |    |          | `--------------------- port
7720
   *       |     |    |          `----------------------- host_end
7721
   *       |     |    `---------------------------------- host_start
7722
   *       |     `--------------------------------------- username_end
7723
   *       `--------------------------------------------- protocol_end
7724
   */
7725
  // These conditions can be made more strict.
7726
123k
  if (protocol_end == url_components::omitted) {
7727
0
    return false;
7728
0
  }
7729
123k
  uint32_t index = protocol_end;
7730
7731
123k
  if (username_end == url_components::omitted) {
7732
0
    return false;
7733
0
  }
7734
123k
  if (username_end < index) {
7735
0
    return false;
7736
0
  }
7737
123k
  index = username_end;
7738
7739
123k
  if (host_start == url_components::omitted) {
7740
0
    return false;
7741
0
  }
7742
123k
  if (host_start < index) {
7743
0
    return false;
7744
0
  }
7745
123k
  index = host_start;
7746
7747
123k
  if (port != url_components::omitted) {
7748
38.0k
    if (port > 0xffff) {
7749
0
      return false;
7750
0
    }
7751
38.0k
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7752
38.0k
    if (index + port_length < index) {
7753
0
      return false;
7754
0
    }
7755
38.0k
    index += port_length;
7756
38.0k
  }
7757
7758
123k
  if (pathname_start == url_components::omitted) {
7759
0
    return false;
7760
0
  }
7761
123k
  if (pathname_start < index) {
7762
0
    return false;
7763
0
  }
7764
123k
  index = pathname_start;
7765
7766
123k
  if (search_start != url_components::omitted) {
7767
84.2k
    if (search_start < index) {
7768
0
      return false;
7769
0
    }
7770
84.2k
    index = search_start;
7771
84.2k
  }
7772
7773
123k
  if (hash_start != url_components::omitted) {
7774
61.3k
    if (hash_start < index) {
7775
0
      return false;
7776
0
    }
7777
61.3k
  }
7778
7779
123k
  return true;
7780
123k
}
7781
7782
}  // namespace ada
7783
#endif
7784
/* end file include/ada/url_components-inl.h */
7785
/* begin file include/ada/url_aggregator.h */
7786
/**
7787
 * @file url_aggregator.h
7788
 * @brief Declaration for the `ada::url_aggregator` class.
7789
 *
7790
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7791
 * URL using a single buffer with component offsets. This is the default and
7792
 * most memory-efficient URL representation in Ada.
7793
 *
7794
 * @see url.h for an alternative representation using separate strings
7795
 */
7796
#ifndef ADA_URL_AGGREGATOR_H
7797
#define ADA_URL_AGGREGATOR_H
7798
7799
#include <ostream>
7800
#include <string>
7801
#include <string_view>
7802
#include <variant>
7803
7804
7805
namespace ada {
7806
7807
namespace parser {}
7808
7809
/**
7810
 * @brief Memory-efficient URL representation using a single buffer.
7811
 *
7812
 * The `url_aggregator` stores the entire normalized URL in a single string
7813
 * buffer and tracks component boundaries using offsets. This design minimizes
7814
 * memory allocations and is ideal for read-mostly access patterns.
7815
 *
7816
 * Getter methods return `std::string_view` pointing into the internal buffer.
7817
 * These views are lightweight (no allocation) but become invalid if the
7818
 * url_aggregator is modified or destroyed.
7819
 *
7820
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7821
 * when any setter is called. Do not use a getter's result as input to a
7822
 * setter on the same object without copying first.
7823
 *
7824
 * @note This is the default URL type returned by `ada::parse()`.
7825
 *
7826
 * @see url For an alternative using separate std::string instances
7827
 */
7828
struct url_aggregator : url_base {
7829
211k
  url_aggregator() = default;
7830
54.2k
  url_aggregator(const url_aggregator& u) = default;
7831
169k
  url_aggregator(url_aggregator&& u) noexcept = default;
7832
16.9k
  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
7833
16.6k
  url_aggregator& operator=(const url_aggregator& u) = default;
7834
434k
  ~url_aggregator() override = default;
7835
7836
  /**
7837
   * The setter functions follow the steps defined in the URL Standard.
7838
   *
7839
   * The url_aggregator has a single buffer that contains the entire normalized
7840
   * URL. The various components are represented as offsets into that buffer.
7841
   * When you call get_pathname(), for example, you get a std::string_view that
7842
   * points into that buffer. If the url_aggregator is modified, the buffer may
7843
   * be reallocated, and the std::string_view you obtained earlier may become
7844
   * invalid. In particular, this implies that you cannot modify the URL using
7845
   * a setter function with a std::string_view that points into the
7846
   * url_aggregator E.g., the following is incorrect:
7847
   * url->set_hostname(url->get_pathname()).
7848
   * You must first copy the pathname to a separate string.
7849
   * std::string pathname(url->get_pathname());
7850
   * url->set_hostname(pathname);
7851
   *
7852
   * The caller is responsible for ensuring that the url_aggregator is not
7853
   * modified while any std::string_view obtained from it is in use.
7854
   */
7855
  bool set_href(std::string_view input);
7856
  bool set_host(std::string_view input);
7857
  bool set_hostname(std::string_view input);
7858
  bool set_protocol(std::string_view input);
7859
  bool set_username(std::string_view input);
7860
  bool set_password(std::string_view input);
7861
  bool set_port(std::string_view input);
7862
  bool set_pathname(std::string_view input);
7863
  void set_search(std::string_view input);
7864
  void set_hash(std::string_view input);
7865
7866
  /**
7867
   * Validates whether the hostname is a valid domain according to RFC 1034.
7868
   * @return `true` if the domain is valid, `false` otherwise.
7869
   */
7870
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7871
7872
  /**
7873
   * Returns the URL's origin (scheme + host + port for special URLs).
7874
   * @return A newly allocated string containing the serialized origin.
7875
   * @see https://url.spec.whatwg.org/#concept-url-origin
7876
   */
7877
  [[nodiscard]] std::string get_origin() const override;
7878
7879
  /**
7880
   * Returns the full serialized URL (the href) as a string_view.
7881
   * Does not allocate memory. The returned view becomes invalid if this
7882
   * url_aggregator is modified or destroyed.
7883
   * @return A string_view into the internal buffer.
7884
   * @see https://url.spec.whatwg.org/#dom-url-href
7885
   */
7886
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7887
      ada_lifetime_bound;
7888
7889
  /**
7890
   * Returns the byte length of the serialized URL without allocating a string.
7891
   * @return Size of the href in bytes.
7892
   */
7893
  [[nodiscard]] constexpr size_t get_href_size() const noexcept;
7894
7895
  /**
7896
   * Returns the URL's username component.
7897
   * Does not allocate memory. The returned view becomes invalid if this
7898
   * url_aggregator is modified or destroyed.
7899
   * @return A string_view of the username.
7900
   * @see https://url.spec.whatwg.org/#dom-url-username
7901
   */
7902
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
7903
7904
  /**
7905
   * Returns the URL's password component.
7906
   * Does not allocate memory. The returned view becomes invalid if this
7907
   * url_aggregator is modified or destroyed.
7908
   * @return A string_view of the password.
7909
   * @see https://url.spec.whatwg.org/#dom-url-password
7910
   */
7911
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
7912
7913
  /**
7914
   * Returns the URL's port as a string (e.g., "8080").
7915
   * Does not allocate memory. Returns empty view if no port is set.
7916
   * The returned view becomes invalid if this url_aggregator is modified.
7917
   * @return A string_view of the port.
7918
   * @see https://url.spec.whatwg.org/#dom-url-port
7919
   */
7920
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
7921
7922
  /**
7923
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
7924
   * Does not allocate memory. Returns empty view if no fragment is set.
7925
   * The returned view becomes invalid if this url_aggregator is modified.
7926
   * @return A string_view of the hash.
7927
   * @see https://url.spec.whatwg.org/#dom-url-hash
7928
   */
7929
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
7930
7931
  /**
7932
   * Returns the URL's host and port (e.g., "example.com:8080").
7933
   * Does not allocate memory. Returns empty view if no host is set.
7934
   * The returned view becomes invalid if this url_aggregator is modified.
7935
   * @return A string_view of host:port.
7936
   * @see https://url.spec.whatwg.org/#dom-url-host
7937
   */
7938
  [[nodiscard]] std::string_view get_host() const ada_lifetime_bound;
7939
7940
  /**
7941
   * Returns the URL's hostname (without port).
7942
   * Does not allocate memory. Returns empty view if no host is set.
7943
   * The returned view becomes invalid if this url_aggregator is modified.
7944
   * @return A string_view of the hostname.
7945
   * @see https://url.spec.whatwg.org/#dom-url-hostname
7946
   */
7947
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
7948
7949
  /**
7950
   * Returns the URL's path component.
7951
   * Does not allocate memory. The returned view becomes invalid if this
7952
   * url_aggregator is modified or destroyed.
7953
   * @return A string_view of the pathname.
7954
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7955
   */
7956
  [[nodiscard]] constexpr std::string_view get_pathname() const
7957
      ada_lifetime_bound;
7958
7959
  /**
7960
   * Returns the byte length of the pathname without creating a string.
7961
   * @return Size of the pathname in bytes.
7962
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7963
   */
7964
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
7965
7966
  /**
7967
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
7968
   * Does not allocate memory. Returns empty view if no query is set.
7969
   * The returned view becomes invalid if this url_aggregator is modified.
7970
   * @return A string_view of the search/query.
7971
   * @see https://url.spec.whatwg.org/#dom-url-search
7972
   */
7973
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
7974
7975
  /**
7976
   * Returns the URL's scheme followed by a colon (e.g., "https:").
7977
   * Does not allocate memory. The returned view becomes invalid if this
7978
   * url_aggregator is modified or destroyed.
7979
   * @return A string_view of the protocol.
7980
   * @see https://url.spec.whatwg.org/#dom-url-protocol
7981
   */
7982
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
7983
7984
  /**
7985
   * Checks if the URL has credentials (non-empty username or password).
7986
   * @return `true` if username or password is non-empty, `false` otherwise.
7987
   */
7988
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
7989
      const noexcept;
7990
7991
  /**
7992
   * Returns the URL component offsets for efficient serialization.
7993
   *
7994
   * The components represent byte offsets into the serialized URL:
7995
   * ```
7996
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7997
   *       |     |    |          | ^^^^|       |   |
7998
   *       |     |    |          | |   |       |   `----- hash_start
7999
   *       |     |    |          | |   |       `--------- search_start
8000
   *       |     |    |          | |   `----------------- pathname_start
8001
   *       |     |    |          | `--------------------- port
8002
   *       |     |    |          `----------------------- host_end
8003
   *       |     |    `---------------------------------- host_start
8004
   *       |     `--------------------------------------- username_end
8005
   *       `--------------------------------------------- protocol_end
8006
   * ```
8007
   * @return A constant reference to the url_components struct.
8008
   * @see https://github.com/servo/rust-url
8009
   */
8010
  [[nodiscard]] ada_really_inline const url_components& get_components()
8011
      const noexcept;
8012
8013
  /**
8014
   * Returns a JSON string representation of this URL for debugging.
8015
   * @return A JSON-formatted string with all URL components.
8016
   */
8017
  [[nodiscard]] std::string to_string() const override;
8018
8019
  /**
8020
   * Returns a visual diagram showing component boundaries in the URL.
8021
   * Useful for debugging and understanding URL structure.
8022
   * @return A multi-line string diagram.
8023
   */
8024
  [[nodiscard]] std::string to_diagram() const;
8025
8026
  /**
8027
   * Validates internal consistency of component offsets (for debugging).
8028
   * @return `true` if offsets are consistent, `false` if corrupted.
8029
   */
8030
  [[nodiscard]] constexpr bool validate() const noexcept;
8031
8032
  /**
8033
   * Checks if the URL has an empty hostname (host is set but empty string).
8034
   * @return `true` if host exists but is empty, `false` otherwise.
8035
   */
8036
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
8037
8038
  /**
8039
   * Checks if the URL has a hostname (including empty hostnames).
8040
   * @return `true` if host is present, `false` otherwise.
8041
   */
8042
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
8043
8044
  /**
8045
   * Checks if the URL has a non-empty username.
8046
   * @return `true` if username is non-empty, `false` otherwise.
8047
   */
8048
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
8049
8050
  /**
8051
   * Checks if the URL has a non-empty password.
8052
   * @return `true` if password is non-empty, `false` otherwise.
8053
   */
8054
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
8055
8056
  /**
8057
   * Checks if the URL has a non-default port explicitly specified.
8058
   * @return `true` if a port is present, `false` otherwise.
8059
   */
8060
  [[nodiscard]] constexpr bool has_port() const noexcept;
8061
8062
  /**
8063
   * Checks if the URL has a password component (may be empty).
8064
   * @return `true` if password is present, `false` otherwise.
8065
   */
8066
  [[nodiscard]] constexpr bool has_password() const noexcept;
8067
8068
  /**
8069
   * Checks if the URL has a fragment/hash component.
8070
   * @return `true` if hash is present, `false` otherwise.
8071
   */
8072
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
8073
8074
  /**
8075
   * Checks if the URL has a query/search component.
8076
   * @return `true` if query is present, `false` otherwise.
8077
   */
8078
  [[nodiscard]] constexpr bool has_search() const noexcept override;
8079
8080
  /**
8081
   * Removes the port from the URL.
8082
   */
8083
  inline void clear_port();
8084
8085
  /**
8086
   * Removes the hash/fragment from the URL.
8087
   */
8088
  inline void clear_hash();
8089
8090
  /**
8091
   * Removes the query/search string from the URL.
8092
   */
8093
  inline void clear_search() override;
8094
8095
 private:
8096
  // helper methods
8097
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
8098
      url_aggregator& url);
8099
  // parse_url methods
8100
  friend url_aggregator parser::parse_url<url_aggregator>(
8101
      std::string_view, const url_aggregator*);
8102
8103
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
8104
      std::string_view, const url_aggregator*);
8105
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
8106
      std::string_view, const url_aggregator*);
8107
  template <class result_type>
8108
  friend bool parser::try_parse_simple_absolute(std::string_view, result_type&);
8109
  template <class result_type>
8110
  friend bool parser::finish_simple_absolute_with_port(std::string_view,
8111
                                                       result_type&,
8112
                                                       ada::scheme::type,
8113
                                                       uint32_t, size_t, size_t,
8114
                                                       size_t, bool);
8115
  template <class result_type>
8116
  friend bool parser::try_parse_simple_relative(std::string_view,
8117
                                                const result_type&,
8118
                                                result_type&);
8119
8120
#if ADA_INCLUDE_URL_PATTERN
8121
  // url_pattern methods
8122
  template <url_pattern_regex::regex_concept regex_provider>
8123
  friend tl::expected<url_pattern<regex_provider>, errors>
8124
  parse_url_pattern_impl(
8125
      std::variant<std::string_view, url_pattern_init>&& input,
8126
      const std::string_view* base_url, const url_pattern_options* options);
8127
#endif  // ADA_INCLUDE_URL_PATTERN
8128
8129
  // components is declared before buffer so that the offset fields land
8130
  // close to url_base in memory, improving cache locality for getter calls.
8131
  // Note: exact cache-line placement is implementation- and platform-dependent.
8132
  url_components components{};
8133
  std::string buffer{};
8134
8135
  /**
8136
   * Returns true if neither the search, nor the hash nor the pathname
8137
   * have been set.
8138
   * @return true if the buffer is ready to receive the path.
8139
   */
8140
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
8141
8142
  inline void add_authority_slashes_if_needed();
8143
8144
  /**
8145
   * To optimize performance, you may indicate how much memory to allocate
8146
   * within this instance.
8147
   */
8148
  constexpr void reserve(uint32_t capacity);
8149
8150
  ada_really_inline size_t parse_port(std::string_view view,
8151
                                      bool check_trailing_content) override;
8152
8153
2.73k
  ada_really_inline size_t parse_port(std::string_view view) override {
8154
2.73k
    return this->parse_port(view, false);
8155
2.73k
  }
8156
8157
  /**
8158
   * Return true on success. The 'in_place' parameter indicates whether the
8159
   * the string_view input is pointing in the buffer. When in_place is false,
8160
   * we must nearly always update the buffer.
8161
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
8162
   */
8163
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
8164
8165
  /**
8166
   * Return true on success.
8167
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
8168
   */
8169
  [[nodiscard]] bool parse_ipv6(std::string_view input);
8170
8171
  /**
8172
   * Return true on success.
8173
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
8174
   */
8175
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
8176
8177
  ada_really_inline void parse_path(std::string_view input);
8178
8179
  /**
8180
   * A URL cannot have a username/password/port if its host is null or the empty
8181
   * string, or its scheme is "file".
8182
   */
8183
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
8184
8185
  /**
8186
   * @private
8187
   * Several setters restore a saved copy of the URL when the mutation pushes
8188
   * the buffer past get_max_input_length(). A setter grows the buffer by at
8189
   * most input_len * 3 (worst-case percent-encoding) plus a small constant for
8190
   * delimiters, so this returns false when that upper bound stays within the
8191
   * limit. It does not account for parse-failure rollbacks, which are
8192
   * unrelated to the length limit.
8193
   */
8194
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
8195
8196
  template <bool override_hostname = false>
8197
  bool set_host_or_hostname(std::string_view input);
8198
8199
  ada_really_inline bool parse_host(std::string_view input);
8200
8201
  inline void update_base_authority(std::string_view base_buffer,
8202
                                    const url_components& base);
8203
  inline void update_unencoded_base_hash(std::string_view input);
8204
  inline void update_base_hostname(std::string_view input);
8205
  inline void update_base_search(std::string_view input);
8206
  inline void update_base_search(std::string_view input,
8207
                                 const uint8_t* query_percent_encode_set);
8208
  inline void update_base_pathname(std::string_view input);
8209
  inline void update_base_username(std::string_view input);
8210
  inline void append_base_username(std::string_view input);
8211
  inline void update_base_password(std::string_view input);
8212
  inline void append_base_password(std::string_view input);
8213
  inline void update_base_port(uint32_t input);
8214
  inline void append_base_pathname(std::string_view input);
8215
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
8216
  constexpr void clear_hostname();
8217
  constexpr void clear_password();
8218
  constexpr void clear_pathname() override;
8219
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
8220
  void delete_dash_dot();
8221
  inline void consume_prepared_path(std::string_view input);
8222
  template <bool has_state_override = false>
8223
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
8224
      std::string_view input);
8225
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
8226
                                                std::string_view input);
8227
  [[nodiscard]] constexpr bool has_authority() const noexcept;
8228
  constexpr void set_protocol_as_file();
8229
  inline void set_scheme(std::string_view new_scheme);
8230
  /**
8231
   * Fast function to set the scheme from a view with a colon in the
8232
   * buffer, does not change type.
8233
   */
8234
  inline void set_scheme_from_view_with_colon(
8235
      std::string_view new_scheme_with_colon);
8236
  inline void copy_scheme(const url_aggregator& u);
8237
8238
  inline void update_host_to_base_host(const std::string_view input);
8239
8240
};  // url_aggregator
8241
8242
inline std::ostream& operator<<(std::ostream& out, const url& u);
8243
}  // namespace ada
8244
8245
#endif
8246
/* end file include/ada/url_aggregator.h */
8247
/* begin file include/ada/url_aggregator-inl.h */
8248
/**
8249
 * @file url_aggregator-inl.h
8250
 * @brief Inline functions for url aggregator
8251
 */
8252
#ifndef ADA_URL_AGGREGATOR_INL_H
8253
#define ADA_URL_AGGREGATOR_INL_H
8254
8255
/* begin file include/ada/unicode-inl.h */
8256
/**
8257
 * @file unicode-inl.h
8258
 * @brief Definitions for unicode operations.
8259
 */
8260
#ifndef ADA_UNICODE_INL_H
8261
#define ADA_UNICODE_INL_H
8262
8263
/**
8264
 * Unicode operations. These functions are not part of our public API and may
8265
 * change at any time.
8266
 *
8267
 * private
8268
 * @namespace ada::unicode
8269
 * @brief Includes the declarations for unicode operations
8270
 */
8271
namespace ada::unicode {
8272
ada_really_inline size_t percent_encode_index(const std::string_view input,
8273
43.6k
                                              const uint8_t character_set[]) {
8274
43.6k
  const char* data = input.data();
8275
43.6k
  const size_t size = input.size();
8276
8277
  // Process 8 bytes at a time using unrolled loop
8278
43.6k
  size_t i = 0;
8279
71.4k
  for (; i + 8 <= size; i += 8) {
8280
45.0k
    unsigned char chunk[8];
8281
45.0k
    std::memcpy(&chunk, data + i,
8282
45.0k
                8);  // entices compiler to unconditionally process 8 characters
8283
8284
    // Check 8 characters at once
8285
300k
    for (size_t j = 0; j < 8; j++) {
8286
272k
      if (character_sets::bit_at(character_set, chunk[j])) {
8287
17.2k
        return i + j;
8288
17.2k
      }
8289
272k
    }
8290
45.0k
  }
8291
8292
  // Handle remaining bytes
8293
50.3k
  for (; i < size; i++) {
8294
35.5k
    if (character_sets::bit_at(character_set, data[i])) {
8295
11.5k
      return i;
8296
11.5k
    }
8297
35.5k
  }
8298
8299
14.8k
  return size;
8300
26.3k
}
8301
}  // namespace ada::unicode
8302
8303
#endif  // ADA_UNICODE_INL_H
8304
/* end file include/ada/unicode-inl.h */
8305
8306
#include <charconv>
8307
#include <cstring>
8308
#include <ostream>
8309
#include <string_view>
8310
8311
namespace ada {
8312
8313
inline void url_aggregator::update_base_authority(
8314
13.2k
    std::string_view base_buffer, const ada::url_components& base) {
8315
13.2k
  std::string_view input = base_buffer.substr(
8316
13.2k
      base.protocol_end, base.host_start - base.protocol_end);
8317
13.2k
  ada_log("url_aggregator::update_base_authority ", input);
8318
8319
13.2k
  bool input_starts_with_dash = input.starts_with("//");
8320
13.2k
  uint32_t diff = components.host_start - components.protocol_end;
8321
8322
13.2k
  buffer.erase(components.protocol_end,
8323
13.2k
               components.host_start - components.protocol_end);
8324
13.2k
  components.username_end = components.protocol_end;
8325
8326
13.2k
  if (input_starts_with_dash) {
8327
12.2k
    input.remove_prefix(2);
8328
12.2k
    diff += 2;  // add "//"
8329
12.2k
    buffer.insert(components.protocol_end, "//");
8330
12.2k
    components.username_end += 2;
8331
12.2k
  }
8332
8333
13.2k
  size_t password_delimiter = input.find(':');
8334
8335
  // Check if input contains both username and password by checking the
8336
  // delimiter: ":" A typical input that contains authority would be "user:pass"
8337
13.2k
  if (password_delimiter != std::string_view::npos) {
8338
    // Insert both username and password
8339
105
    std::string_view username = input.substr(0, password_delimiter);
8340
105
    std::string_view password = input.substr(password_delimiter + 1);
8341
8342
105
    buffer.insert(components.protocol_end + diff, username);
8343
105
    diff += uint32_t(username.size());
8344
105
    buffer.insert(components.protocol_end + diff, ":");
8345
105
    components.username_end = components.protocol_end + diff;
8346
105
    buffer.insert(components.protocol_end + diff + 1, password);
8347
105
    diff += uint32_t(password.size()) + 1;
8348
13.0k
  } else if (!input.empty()) {
8349
    // Insert only username
8350
164
    buffer.insert(components.protocol_end + diff, input);
8351
164
    components.username_end =
8352
164
        components.protocol_end + diff + uint32_t(input.size());
8353
164
    diff += uint32_t(input.size());
8354
164
  }
8355
8356
13.2k
  components.host_start += diff;
8357
8358
13.2k
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
8359
0
    buffer.insert(components.host_start, "@");
8360
0
    diff++;
8361
0
  }
8362
13.2k
  components.host_end += diff;
8363
13.2k
  components.pathname_start += diff;
8364
13.2k
  if (components.search_start != url_components::omitted) {
8365
0
    components.search_start += diff;
8366
0
  }
8367
13.2k
  if (components.hash_start != url_components::omitted) {
8368
0
    components.hash_start += diff;
8369
0
  }
8370
13.2k
}
8371
8372
42.6k
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
8373
42.6k
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
8374
42.6k
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
8375
42.6k
          " bytes] components.hash_start = ", components.hash_start);
8376
42.6k
  ADA_ASSERT_TRUE(validate());
8377
42.6k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8378
42.6k
  if (components.hash_start != url_components::omitted) {
8379
387
    buffer.resize(components.hash_start);
8380
387
  }
8381
42.6k
  components.hash_start = uint32_t(buffer.size());
8382
42.6k
  buffer += "#";
8383
42.6k
  bool encoding_required = unicode::percent_encode<true>(
8384
42.6k
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
8385
  // When encoding_required is false, then buffer is left unchanged, and percent
8386
  // encoding was not deemed required.
8387
42.6k
  if (!encoding_required) {
8388
33.5k
    buffer.append(input);
8389
33.5k
  }
8390
42.6k
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
8391
42.6k
          buffer, "' [", buffer.size(), " bytes]");
8392
42.6k
  ADA_ASSERT_TRUE(validate());
8393
42.6k
}
8394
8395
ada_really_inline uint32_t url_aggregator::replace_and_resize(
8396
204k
    uint32_t start, uint32_t end, std::string_view input) {
8397
204k
  uint32_t current_length = end - start;
8398
204k
  uint32_t input_size = uint32_t(input.size());
8399
204k
  uint32_t new_difference = input_size - current_length;
8400
8401
204k
  if (current_length == 0) {
8402
140k
    buffer.insert(start, input);
8403
140k
  } else if (input_size == current_length) {
8404
3.07k
    if (input_size != 0) {
8405
3.07k
      std::memmove(buffer.data() + start, input.data(), input_size);
8406
3.07k
    }
8407
60.1k
  } else if (input_size < current_length) {
8408
25.2k
    buffer.erase(start, current_length - input_size);
8409
25.2k
    buffer.replace(start, input_size, input);
8410
34.9k
  } else {
8411
34.9k
    buffer.replace(start, current_length, input.substr(0, current_length));
8412
34.9k
    buffer.insert(start + current_length, input.substr(current_length));
8413
34.9k
  }
8414
8415
204k
  return new_difference;
8416
204k
}
8417
8418
94.9k
inline void url_aggregator::update_base_hostname(const std::string_view input) {
8419
94.9k
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
8420
94.9k
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
8421
94.9k
  ADA_ASSERT_TRUE(validate());
8422
94.9k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8423
8424
  // This next line is required for when parsing a URL like `foo://`
8425
94.9k
  add_authority_slashes_if_needed();
8426
8427
94.9k
  bool has_credentials = components.protocol_end + 2 < components.host_start;
8428
94.9k
  uint32_t new_difference =
8429
94.9k
      replace_and_resize(components.host_start, components.host_end, input);
8430
8431
94.9k
  if (has_credentials) {
8432
41.9k
    buffer.insert(components.host_start, "@");
8433
41.9k
    new_difference++;
8434
41.9k
  }
8435
94.9k
  components.host_end += new_difference;
8436
94.9k
  components.pathname_start += new_difference;
8437
94.9k
  if (components.search_start != url_components::omitted) {
8438
5.88k
    components.search_start += new_difference;
8439
5.88k
  }
8440
94.9k
  if (components.hash_start != url_components::omitted) {
8441
5.92k
    components.hash_start += new_difference;
8442
5.92k
  }
8443
94.9k
  ADA_ASSERT_TRUE(validate());
8444
94.9k
}
8445
8446
[[nodiscard]] ada_really_inline uint32_t
8447
133k
url_aggregator::get_pathname_length() const noexcept {
8448
133k
  ada_log("url_aggregator::get_pathname_length");
8449
133k
  uint32_t ending_index = uint32_t(buffer.size());
8450
133k
  if (components.search_start != url_components::omitted) {
8451
23.4k
    ending_index = components.search_start;
8452
110k
  } else if (components.hash_start != url_components::omitted) {
8453
1.01k
    ending_index = components.hash_start;
8454
1.01k
  }
8455
133k
  return ending_index - components.pathname_start;
8456
133k
}
8457
8458
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
8459
41.8k
    const noexcept {
8460
41.8k
  return buffer.size() == components.pathname_start;
8461
41.8k
}
8462
8463
8.90k
inline void url_aggregator::update_base_search(std::string_view input) {
8464
8.90k
  ada_log("url_aggregator::update_base_search ", input);
8465
8.90k
  ADA_ASSERT_TRUE(validate());
8466
8.90k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8467
8.90k
  if (input.empty()) {
8468
0
    clear_search();
8469
0
    return;
8470
0
  }
8471
8472
8.90k
  if (input[0] == '?') {
8473
8.90k
    input.remove_prefix(1);
8474
8.90k
  }
8475
8476
8.90k
  if (components.hash_start == url_components::omitted) {
8477
8.90k
    if (components.search_start == url_components::omitted) {
8478
8.90k
      components.search_start = uint32_t(buffer.size());
8479
8.90k
      buffer += "?";
8480
8.90k
    } else {
8481
0
      buffer.resize(components.search_start + 1);
8482
0
    }
8483
8484
8.90k
    buffer.append(input);
8485
8.90k
  } else if (components.search_start != url_components::omitted) {
8486
0
    const uint32_t difference = replace_and_resize(
8487
0
        components.search_start + 1, components.hash_start, input);
8488
0
    components.hash_start += difference;
8489
0
  } else {
8490
0
    components.search_start = components.hash_start;
8491
0
    buffer.insert(components.search_start, input.size() + 1, '?');
8492
0
    if (!input.empty()) {
8493
0
      std::memmove(buffer.data() + components.search_start + 1, input.data(),
8494
0
                   input.size());
8495
0
    }
8496
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8497
0
  }
8498
8499
8.90k
  ADA_ASSERT_TRUE(validate());
8500
8.90k
}
8501
8502
inline void url_aggregator::update_base_search(
8503
70.3k
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8504
70.3k
  ada_log("url_aggregator::update_base_search ", input,
8505
70.3k
          " with encoding parameter ", to_string(), "\n", to_diagram());
8506
70.3k
  ADA_ASSERT_TRUE(validate());
8507
70.3k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8508
8509
70.3k
  if (components.hash_start == url_components::omitted) {
8510
69.1k
    if (components.search_start == url_components::omitted) {
8511
49.9k
      components.search_start = uint32_t(buffer.size());
8512
49.9k
      buffer += "?";
8513
49.9k
    } else {
8514
19.2k
      buffer.resize(components.search_start + 1);
8515
19.2k
    }
8516
8517
69.1k
    bool encoding_required =
8518
69.1k
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8519
    // When encoding_required is false, then buffer is left unchanged, and
8520
    // percent encoding was not deemed required.
8521
69.1k
    if (!encoding_required) {
8522
52.3k
      buffer.append(input);
8523
52.3k
    }
8524
69.1k
  } else {
8525
1.20k
    const size_t idx =
8526
1.20k
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8527
1.20k
    std::string encoded;
8528
1.20k
    std::string_view replacement = input;
8529
1.20k
    if (idx != input.size()) {
8530
170
      encoded =
8531
170
          ada::unicode::percent_encode(input, query_percent_encode_set, idx);
8532
170
      replacement = encoded;
8533
170
    }
8534
8535
1.20k
    if (components.search_start != url_components::omitted) {
8536
1.16k
      const uint32_t difference = replace_and_resize(
8537
1.16k
          components.search_start + 1, components.hash_start, replacement);
8538
1.16k
      components.hash_start += difference;
8539
1.16k
    } else {
8540
40
      components.search_start = components.hash_start;
8541
40
      buffer.insert(components.search_start, replacement.size() + 1, '?');
8542
40
      if (!replacement.empty()) {
8543
28
        std::memmove(buffer.data() + components.search_start + 1,
8544
28
                     replacement.data(), replacement.size());
8545
28
      }
8546
40
      components.hash_start +=
8547
40
          uint32_t(replacement.size() + 1);  // Do not forget `?`
8548
40
    }
8549
1.20k
  }
8550
8551
70.3k
  ADA_ASSERT_TRUE(validate());
8552
70.3k
}
8553
8554
83.8k
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8555
83.8k
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8556
83.8k
          " bytes] \n", to_diagram());
8557
83.8k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8558
83.8k
  ADA_ASSERT_TRUE(validate());
8559
8560
83.8k
  const bool begins_with_dashdash = input.starts_with("//");
8561
83.8k
  if (!begins_with_dashdash && has_dash_dot()) {
8562
    // We must delete the ./
8563
36
    delete_dash_dot();
8564
36
  }
8565
8566
83.8k
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8567
290
      !has_dash_dot()) {
8568
    // If url's host is null, url does not have an opaque path, url's path's
8569
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8570
    // output.
8571
244
    buffer.insert(components.pathname_start, "/.");
8572
244
    components.pathname_start += 2;
8573
244
    if (components.search_start != url_components::omitted) {
8574
0
      components.search_start += 2;
8575
0
    }
8576
244
    if (components.hash_start != url_components::omitted) {
8577
0
      components.hash_start += 2;
8578
0
    }
8579
244
  }
8580
8581
83.8k
  uint32_t difference = replace_and_resize(
8582
83.8k
      components.pathname_start,
8583
83.8k
      components.pathname_start + get_pathname_length(), input);
8584
83.8k
  if (components.search_start != url_components::omitted) {
8585
1.62k
    components.search_start += difference;
8586
1.62k
  }
8587
83.8k
  if (components.hash_start != url_components::omitted) {
8588
1.67k
    components.hash_start += difference;
8589
1.67k
  }
8590
83.8k
  ADA_ASSERT_TRUE(validate());
8591
83.8k
}
8592
8593
27
inline void url_aggregator::append_base_pathname(const std::string_view input) {
8594
27
  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
8595
27
          "\n", to_diagram());
8596
27
  ADA_ASSERT_TRUE(validate());
8597
27
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8598
#if ADA_DEVELOPMENT_CHECKS
8599
  // computing the expected password.
8600
  std::string path_expected(get_pathname());
8601
  path_expected.append(input);
8602
#endif  // ADA_DEVELOPMENT_CHECKS
8603
27
  uint32_t ending_index = uint32_t(buffer.size());
8604
27
  if (components.search_start != url_components::omitted) {
8605
0
    ending_index = components.search_start;
8606
27
  } else if (components.hash_start != url_components::omitted) {
8607
0
    ending_index = components.hash_start;
8608
0
  }
8609
27
  buffer.insert(ending_index, input);
8610
8611
27
  if (components.search_start != url_components::omitted) {
8612
0
    components.search_start += uint32_t(input.size());
8613
0
  }
8614
27
  if (components.hash_start != url_components::omitted) {
8615
0
    components.hash_start += uint32_t(input.size());
8616
0
  }
8617
#if ADA_DEVELOPMENT_CHECKS
8618
  std::string path_after = std::string(get_pathname());
8619
  ADA_ASSERT_EQUAL(
8620
      path_expected, path_after,
8621
      "append_base_pathname problem after inserting " + std::string(input));
8622
#endif  // ADA_DEVELOPMENT_CHECKS
8623
27
  ADA_ASSERT_TRUE(validate());
8624
27
}
8625
8626
23.8k
inline void url_aggregator::update_base_username(const std::string_view input) {
8627
23.8k
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8628
23.8k
          "\n", to_diagram());
8629
23.8k
  ADA_ASSERT_TRUE(validate());
8630
23.8k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8631
8632
23.8k
  add_authority_slashes_if_needed();
8633
8634
23.8k
  bool has_password = has_non_empty_password();
8635
23.8k
  bool host_starts_with_at = buffer.size() > components.host_start &&
8636
23.8k
                             buffer[components.host_start] == '@';
8637
23.8k
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8638
23.8k
                                     components.username_end, input);
8639
8640
23.8k
  components.username_end += diff;
8641
23.8k
  components.host_start += diff;
8642
8643
23.8k
  if (!input.empty() && !host_starts_with_at) {
8644
15.4k
    buffer.insert(components.host_start, "@");
8645
15.4k
    diff++;
8646
15.4k
  } else if (input.empty() && host_starts_with_at && !has_password) {
8647
    // Input is empty, there is no password, and we need to remove "@" from
8648
    // hostname
8649
1.39k
    buffer.erase(components.host_start, 1);
8650
1.39k
    diff--;
8651
1.39k
  }
8652
8653
23.8k
  components.host_end += diff;
8654
23.8k
  components.pathname_start += diff;
8655
23.8k
  if (components.search_start != url_components::omitted) {
8656
1.78k
    components.search_start += diff;
8657
1.78k
  }
8658
23.8k
  if (components.hash_start != url_components::omitted) {
8659
1.78k
    components.hash_start += diff;
8660
1.78k
  }
8661
23.8k
  ADA_ASSERT_TRUE(validate());
8662
23.8k
}
8663
8664
43.5k
inline void url_aggregator::append_base_username(const std::string_view input) {
8665
43.5k
  ada_log("url_aggregator::append_base_username ", input);
8666
43.5k
  ADA_ASSERT_TRUE(validate());
8667
43.5k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8668
#if ADA_DEVELOPMENT_CHECKS
8669
  // computing the expected password.
8670
  std::string username_expected(get_username());
8671
  username_expected.append(input);
8672
#endif  // ADA_DEVELOPMENT_CHECKS
8673
43.5k
  add_authority_slashes_if_needed();
8674
8675
  // If input is empty, do nothing.
8676
43.5k
  if (input.empty()) {
8677
9.41k
    return;
8678
9.41k
  }
8679
8680
34.1k
  uint32_t difference = uint32_t(input.size());
8681
34.1k
  buffer.insert(components.username_end, input);
8682
34.1k
  components.username_end += difference;
8683
34.1k
  components.host_start += difference;
8684
8685
34.1k
  if (buffer[components.host_start] != '@' &&
8686
22.2k
      components.host_start != components.host_end) {
8687
22.2k
    buffer.insert(components.host_start, "@");
8688
22.2k
    difference++;
8689
22.2k
  }
8690
8691
34.1k
  components.host_end += difference;
8692
34.1k
  components.pathname_start += difference;
8693
34.1k
  if (components.search_start != url_components::omitted) {
8694
0
    components.search_start += difference;
8695
0
  }
8696
34.1k
  if (components.hash_start != url_components::omitted) {
8697
0
    components.hash_start += difference;
8698
0
  }
8699
#if ADA_DEVELOPMENT_CHECKS
8700
  std::string username_after(get_username());
8701
  ADA_ASSERT_EQUAL(
8702
      username_expected, username_after,
8703
      "append_base_username problem after inserting " + std::string(input));
8704
#endif  // ADA_DEVELOPMENT_CHECKS
8705
34.1k
  ADA_ASSERT_TRUE(validate());
8706
34.1k
}
8707
8708
4.86k
constexpr void url_aggregator::clear_password() {
8709
4.86k
  ada_log("url_aggregator::clear_password ", to_string());
8710
4.86k
  ADA_ASSERT_TRUE(validate());
8711
4.86k
  if (!has_password()) {
8712
3.32k
    return;
8713
3.32k
  }
8714
8715
1.54k
  uint32_t diff = components.host_start - components.username_end;
8716
1.54k
  buffer.erase(components.username_end, diff);
8717
1.54k
  components.host_start -= diff;
8718
1.54k
  components.host_end -= diff;
8719
1.54k
  components.pathname_start -= diff;
8720
1.54k
  if (components.search_start != url_components::omitted) {
8721
1.47k
    components.search_start -= diff;
8722
1.47k
  }
8723
1.54k
  if (components.hash_start != url_components::omitted) {
8724
1.47k
    components.hash_start -= diff;
8725
1.47k
  }
8726
1.54k
}
8727
8728
20.6k
inline void url_aggregator::update_base_password(const std::string_view input) {
8729
20.6k
  ada_log("url_aggregator::update_base_password ", input);
8730
20.6k
  ADA_ASSERT_TRUE(validate());
8731
20.6k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8732
8733
20.6k
  add_authority_slashes_if_needed();
8734
8735
  // TODO: Optimization opportunity. Merge the following removal functions.
8736
20.6k
  if (input.empty()) {
8737
4.86k
    clear_password();
8738
8739
    // Remove username too, if it is empty.
8740
4.86k
    if (!has_non_empty_username()) {
8741
3.34k
      update_base_username("");
8742
3.34k
    }
8743
8744
4.86k
    return;
8745
4.86k
  }
8746
8747
15.8k
  bool password_exists = has_password();
8748
15.8k
  uint32_t difference = uint32_t(input.size());
8749
8750
15.8k
  if (password_exists) {
8751
344
    difference = replace_and_resize(components.username_end + 1,
8752
344
                                    components.host_start, input);
8753
15.4k
  } else {
8754
15.4k
    buffer.insert(components.username_end, input.size() + 1, ':');
8755
15.4k
    std::memmove(buffer.data() + components.username_end + 1, input.data(),
8756
15.4k
                 input.size());
8757
15.4k
    difference++;
8758
15.4k
  }
8759
15.8k
  components.host_start += difference;
8760
8761
  // The following line is required to add "@" to hostname. When updating
8762
  // password if hostname does not start with "@", it is "update_base_password"s
8763
  // responsibility to set it.
8764
15.8k
  if (buffer[components.host_start] != '@') {
8765
14
    buffer.insert(components.host_start, "@");
8766
14
    difference++;
8767
14
  }
8768
8769
15.8k
  components.host_end += difference;
8770
15.8k
  components.pathname_start += difference;
8771
15.8k
  if (components.search_start != url_components::omitted) {
8772
344
    components.search_start += difference;
8773
344
  }
8774
15.8k
  if (components.hash_start != url_components::omitted) {
8775
346
    components.hash_start += difference;
8776
346
  }
8777
15.8k
  ADA_ASSERT_TRUE(validate());
8778
15.8k
}
8779
8780
33.4k
inline void url_aggregator::append_base_password(const std::string_view input) {
8781
33.4k
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8782
33.4k
          "\n", to_diagram());
8783
33.4k
  ADA_ASSERT_TRUE(validate());
8784
33.4k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8785
#if ADA_DEVELOPMENT_CHECKS
8786
  // computing the expected password.
8787
  std::string password_expected = std::string(get_password());
8788
  password_expected.append(input);
8789
#endif  // ADA_DEVELOPMENT_CHECKS
8790
33.4k
  add_authority_slashes_if_needed();
8791
8792
  // If input is empty, do nothing.
8793
33.4k
  if (input.empty()) {
8794
4.44k
    return;
8795
4.44k
  }
8796
8797
28.9k
  uint32_t difference = uint32_t(input.size());
8798
28.9k
  if (has_password()) {
8799
7.71k
    buffer.insert(components.host_start, input);
8800
21.2k
  } else {
8801
21.2k
    difference++;  // Increment for ":"
8802
21.2k
    buffer.insert(components.username_end, ":");
8803
21.2k
    buffer.insert(components.username_end + 1, input);
8804
21.2k
  }
8805
28.9k
  components.host_start += difference;
8806
8807
  // The following line is required to add "@" to hostname. When updating
8808
  // password if hostname does not start with "@", it is "append_base_password"s
8809
  // responsibility to set it.
8810
28.9k
  if (buffer[components.host_start] != '@') {
8811
773
    buffer.insert(components.host_start, "@");
8812
773
    difference++;
8813
773
  }
8814
8815
28.9k
  components.host_end += difference;
8816
28.9k
  components.pathname_start += difference;
8817
28.9k
  if (components.search_start != url_components::omitted) {
8818
0
    components.search_start += difference;
8819
0
  }
8820
28.9k
  if (components.hash_start != url_components::omitted) {
8821
0
    components.hash_start += difference;
8822
0
  }
8823
#if ADA_DEVELOPMENT_CHECKS
8824
  std::string password_after(get_password());
8825
  ADA_ASSERT_EQUAL(
8826
      password_expected, password_after,
8827
      "append_base_password problem after inserting " + std::string(input));
8828
#endif  // ADA_DEVELOPMENT_CHECKS
8829
28.9k
  ADA_ASSERT_TRUE(validate());
8830
28.9k
}
8831
8832
37.0k
inline void url_aggregator::update_base_port(uint32_t input) {
8833
37.0k
  ada_log("url_aggregator::update_base_port");
8834
37.0k
  ADA_ASSERT_TRUE(validate());
8835
37.0k
  if (input == url_components::omitted) {
8836
13.0k
    clear_port();
8837
13.0k
    return;
8838
13.0k
  }
8839
  // calling std::to_string(input.value()) is unfortunate given that the port
8840
  // value is probably already available as a string.
8841
23.9k
  std::string value = helpers::concat(":", std::to_string(input));
8842
23.9k
  uint32_t difference = uint32_t(value.size());
8843
8844
23.9k
  if (components.port != url_components::omitted) {
8845
308
    difference -= components.pathname_start - components.host_end;
8846
308
    buffer.erase(components.host_end,
8847
308
                 components.pathname_start - components.host_end);
8848
308
  }
8849
8850
23.9k
  buffer.insert(components.host_end, value);
8851
23.9k
  components.pathname_start += difference;
8852
23.9k
  if (components.search_start != url_components::omitted) {
8853
1.26k
    components.search_start += difference;
8854
1.26k
  }
8855
23.9k
  if (components.hash_start != url_components::omitted) {
8856
1.27k
    components.hash_start += difference;
8857
1.27k
  }
8858
23.9k
  components.port = input;
8859
23.9k
  ADA_ASSERT_TRUE(validate());
8860
23.9k
}
8861
8862
40.7k
inline void url_aggregator::clear_port() {
8863
40.7k
  ada_log("url_aggregator::clear_port");
8864
40.7k
  ADA_ASSERT_TRUE(validate());
8865
40.7k
  if (components.port == url_components::omitted) {
8866
37.2k
    return;
8867
37.2k
  }
8868
3.49k
  uint32_t length = components.pathname_start - components.host_end;
8869
3.49k
  buffer.erase(components.host_end, length);
8870
3.49k
  components.pathname_start -= length;
8871
3.49k
  if (components.search_start != url_components::omitted) {
8872
3.45k
    components.search_start -= length;
8873
3.45k
  }
8874
3.49k
  if (components.hash_start != url_components::omitted) {
8875
3.46k
    components.hash_start -= length;
8876
3.46k
  }
8877
3.49k
  components.port = url_components::omitted;
8878
3.49k
  ADA_ASSERT_TRUE(validate());
8879
3.49k
}
8880
8881
13.2k
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8882
13.2k
  ada_log("url_aggregator::retrieve_base_port");
8883
13.2k
  return components.port;
8884
13.2k
}
8885
8886
31.7k
inline void url_aggregator::clear_search() {
8887
31.7k
  ada_log("url_aggregator::clear_search");
8888
31.7k
  ADA_ASSERT_TRUE(validate());
8889
31.7k
  if (components.search_start == url_components::omitted) {
8890
8.22k
    return;
8891
8.22k
  }
8892
8893
23.5k
  if (components.hash_start == url_components::omitted) {
8894
6.68k
    buffer.resize(components.search_start);
8895
16.8k
  } else {
8896
16.8k
    buffer.erase(components.search_start,
8897
16.8k
                 components.hash_start - components.search_start);
8898
16.8k
    components.hash_start = components.search_start;
8899
16.8k
  }
8900
8901
23.5k
  components.search_start = url_components::omitted;
8902
8903
#if ADA_DEVELOPMENT_CHECKS
8904
  ADA_ASSERT_EQUAL(get_search(), "",
8905
                   "search should have been cleared on buffer=" + buffer +
8906
                       " with " + components.to_string() + "\n" + to_diagram());
8907
#endif
8908
23.5k
  ADA_ASSERT_TRUE(validate());
8909
23.5k
}
8910
8911
22.1k
inline void url_aggregator::clear_hash() {
8912
22.1k
  ada_log("url_aggregator::clear_hash");
8913
22.1k
  ADA_ASSERT_TRUE(validate());
8914
22.1k
  if (components.hash_start == url_components::omitted) {
8915
3.66k
    return;
8916
3.66k
  }
8917
18.5k
  buffer.resize(components.hash_start);
8918
18.5k
  components.hash_start = url_components::omitted;
8919
8920
#if ADA_DEVELOPMENT_CHECKS
8921
  ADA_ASSERT_EQUAL(get_hash(), "",
8922
                   "hash should have been cleared on buffer=" + buffer +
8923
                       " with " + components.to_string() + "\n" + to_diagram());
8924
#endif
8925
18.5k
  ADA_ASSERT_TRUE(validate());
8926
18.5k
}
8927
8928
21.7k
constexpr void url_aggregator::clear_pathname() {
8929
21.7k
  ada_log("url_aggregator::clear_pathname");
8930
21.7k
  ADA_ASSERT_TRUE(validate());
8931
21.7k
  uint32_t ending_index = uint32_t(buffer.size());
8932
21.7k
  if (components.search_start != url_components::omitted) {
8933
1.62k
    ending_index = components.search_start;
8934
20.1k
  } else if (components.hash_start != url_components::omitted) {
8935
66
    ending_index = components.hash_start;
8936
66
  }
8937
21.7k
  uint32_t pathname_length = ending_index - components.pathname_start;
8938
21.7k
  buffer.erase(components.pathname_start, pathname_length);
8939
21.7k
  uint32_t difference = pathname_length;
8940
21.7k
  if (components.pathname_start == components.host_end + 2 &&
8941
848
      buffer[components.host_end] == '/' &&
8942
0
      buffer[components.host_end + 1] == '.') {
8943
0
    components.pathname_start -= 2;
8944
0
    buffer.erase(components.host_end, 2);
8945
0
    difference += 2;
8946
0
  }
8947
21.7k
  if (components.search_start != url_components::omitted) {
8948
1.62k
    components.search_start -= difference;
8949
1.62k
  }
8950
21.7k
  if (components.hash_start != url_components::omitted) {
8951
1.67k
    components.hash_start -= difference;
8952
1.67k
  }
8953
21.7k
  ada_log("url_aggregator::clear_pathname completed, running checks...");
8954
#if ADA_DEVELOPMENT_CHECKS
8955
  ADA_ASSERT_EQUAL(get_pathname(), "",
8956
                   "pathname should have been cleared on buffer=" + buffer +
8957
                       " with " + components.to_string() + "\n" + to_diagram());
8958
#endif
8959
21.7k
  ADA_ASSERT_TRUE(validate());
8960
21.7k
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
8961
21.7k
}
8962
8963
200
constexpr void url_aggregator::clear_hostname() {
8964
200
  ada_log("url_aggregator::clear_hostname");
8965
200
  ADA_ASSERT_TRUE(validate());
8966
200
  if (!has_authority()) {
8967
0
    return;
8968
0
  }
8969
200
  ADA_ASSERT_TRUE(has_authority());
8970
8971
200
  uint32_t hostname_length = components.host_end - components.host_start;
8972
200
  uint32_t start = components.host_start;
8973
8974
  // If hostname starts with "@", we should not remove that character.
8975
200
  if (hostname_length > 0 && buffer[start] == '@') {
8976
0
    start++;
8977
0
    hostname_length--;
8978
0
  }
8979
200
  buffer.erase(start, hostname_length);
8980
200
  components.host_end = start;
8981
200
  components.pathname_start -= hostname_length;
8982
200
  if (components.search_start != url_components::omitted) {
8983
125
    components.search_start -= hostname_length;
8984
125
  }
8985
200
  if (components.hash_start != url_components::omitted) {
8986
126
    components.hash_start -= hostname_length;
8987
126
  }
8988
#if ADA_DEVELOPMENT_CHECKS
8989
  ADA_ASSERT_EQUAL(get_hostname(), "",
8990
                   "hostname should have been cleared on buffer=" + buffer +
8991
                       " with " + components.to_string() + "\n" + to_diagram());
8992
#endif
8993
200
  ADA_ASSERT_TRUE(has_authority());
8994
200
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
8995
200
                   "hostname should have been cleared on buffer=" + buffer +
8996
200
                       " with " + components.to_string() + "\n" + to_diagram());
8997
200
  ADA_ASSERT_TRUE(validate());
8998
200
}
8999
9000
69.7k
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
9001
69.7k
  ada_log("url_aggregator::has_hash");
9002
69.7k
  return components.hash_start != url_components::omitted;
9003
69.7k
}
9004
9005
84.1k
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
9006
84.1k
  ada_log("url_aggregator::has_search");
9007
84.1k
  return components.search_start != url_components::omitted;
9008
84.1k
}
9009
9010
107k
constexpr bool url_aggregator::has_credentials() const noexcept {
9011
107k
  ada_log("url_aggregator::has_credentials");
9012
107k
  return has_non_empty_username() || has_non_empty_password();
9013
107k
}
9014
9015
72.4k
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
9016
72.4k
  ada_log("url_aggregator::cannot_have_credentials_or_port");
9017
72.4k
  return type == ada::scheme::type::FILE ||
9018
68.7k
         components.host_start == components.host_end;
9019
72.4k
}
9020
9021
[[nodiscard]] ada_really_inline const ada::url_components&
9022
63.0k
url_aggregator::get_components() const noexcept {
9023
63.0k
  return components;
9024
63.0k
}
9025
9026
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
9027
390k
    const noexcept {
9028
390k
  ada_log("url_aggregator::has_authority");
9029
  // Performance: instead of doing this potentially expensive check, we could
9030
  // have a boolean in the struct.
9031
390k
  return components.protocol_end + 2 <= components.host_start &&
9032
319k
         buffer[components.protocol_end] == '/' &&
9033
319k
         buffer[components.protocol_end + 1] == '/';
9034
390k
}
9035
9036
216k
inline void ada::url_aggregator::add_authority_slashes_if_needed() {
9037
216k
  ada_log("url_aggregator::add_authority_slashes_if_needed");
9038
216k
  ADA_ASSERT_TRUE(validate());
9039
  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
9040
  // to insert
9041
  // `//` initially to the buffer, since it depends on the hostname existence.
9042
216k
  if (has_authority()) {
9043
163k
    return;
9044
163k
  }
9045
  // Performance: the common case is components.protocol_end == buffer.size()
9046
  // Optimization opportunity: in many cases, the "//" is part of the input and
9047
  // the insert could be fused with another insert.
9048
52.4k
  buffer.insert(components.protocol_end, "//");
9049
52.4k
  components.username_end += 2;
9050
52.4k
  components.host_start += 2;
9051
52.4k
  components.host_end += 2;
9052
52.4k
  components.pathname_start += 2;
9053
52.4k
  if (components.search_start != url_components::omitted) {
9054
0
    components.search_start += 2;
9055
0
  }
9056
52.4k
  if (components.hash_start != url_components::omitted) {
9057
0
    components.hash_start += 2;
9058
0
  }
9059
52.4k
  ADA_ASSERT_TRUE(validate());
9060
52.4k
}
9061
9062
113k
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
9063
113k
  buffer.reserve(capacity);
9064
113k
}
9065
9066
235k
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
9067
235k
  ada_log("url_aggregator::has_non_empty_username");
9068
235k
  return components.protocol_end + 2 < components.username_end;
9069
235k
}
9070
9071
221k
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
9072
221k
  ada_log("url_aggregator::has_non_empty_password");
9073
221k
  return components.host_start > components.username_end;
9074
221k
}
9075
9076
99.5k
constexpr bool url_aggregator::has_password() const noexcept {
9077
99.5k
  ada_log("url_aggregator::has_password");
9078
  // This function does not care about the length of the password
9079
99.5k
  return components.host_start > components.username_end &&
9080
25.4k
         buffer[components.username_end] == ':';
9081
99.5k
}
9082
9083
49.8k
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
9084
49.8k
  if (!has_hostname()) {
9085
5.73k
    return false;
9086
5.73k
  }
9087
44.1k
  if (components.host_start == components.host_end) {
9088
1.60k
    return true;
9089
1.60k
  }
9090
42.5k
  if (components.host_end > components.host_start + 1) {
9091
38.8k
    return false;
9092
38.8k
  }
9093
3.64k
  return components.username_end != components.host_start;
9094
42.5k
}
9095
9096
170k
constexpr bool url_aggregator::has_hostname() const noexcept {
9097
170k
  return has_authority();
9098
170k
}
9099
9100
69.7k
constexpr bool url_aggregator::has_port() const noexcept {
9101
69.7k
  ada_log("url_aggregator::has_port");
9102
  // A URL cannot have a username/password/port if its host is null or the empty
9103
  // string, or its scheme is "file".
9104
69.7k
  return has_hostname() && components.pathname_start != components.host_end;
9105
69.7k
}
9106
9107
98.7k
[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
9108
  // If url's host is null, url does not have an opaque path, url's path's size
9109
  // is greater than 1, and url's path[0] is the empty string, then append
9110
  // U+002F (/) followed by U+002E (.) to output.
9111
98.7k
  ada_log("url_aggregator::has_dash_dot");
9112
#if ADA_DEVELOPMENT_CHECKS
9113
  // If pathname_start and host_end are exactly two characters apart, then we
9114
  // either have a one-digit port such as http://test.com:5?param=1 or else we
9115
  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
9116
  if (components.pathname_start == components.host_end + 2) {
9117
    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
9118
                     buffer[components.host_end + 1] == '.') ||
9119
                    (buffer[components.host_end] == ':' &&
9120
                     checkers::is_digit(buffer[components.host_end + 1])));
9121
  }
9122
  if (components.pathname_start == components.host_end + 2 &&
9123
      buffer[components.host_end] == '/' &&
9124
      buffer[components.host_end + 1] == '.') {
9125
    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
9126
    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
9127
    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
9128
  }
9129
#endif
9130
  // Performance: it should be uncommon for components.pathname_start ==
9131
  // components.host_end + 2 to be true. So we put this check first in the
9132
  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
9133
  // more expensive, but should be uncommon.
9134
98.7k
  return components.pathname_start == components.host_end + 2 &&
9135
1.65k
         !has_opaque_path && buffer[components.host_end] == '/' &&
9136
82
         buffer[components.host_end + 1] == '.';
9137
98.7k
}
9138
9139
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
9140
254k
    const noexcept ada_lifetime_bound {
9141
254k
  ada_log("url_aggregator::get_href");
9142
254k
  return buffer;
9143
254k
}
9144
9145
26.7k
[[nodiscard]] constexpr size_t url_aggregator::get_href_size() const noexcept {
9146
26.7k
  return buffer.size();
9147
26.7k
}
9148
9149
ada_really_inline size_t
9150
27.7k
url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
9151
27.7k
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
9152
27.7k
  if (!view.empty() && view[0] == '-') {
9153
22
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
9154
22
    is_valid = false;
9155
22
    return 0;
9156
22
  }
9157
27.7k
  uint16_t parsed_port{};
9158
27.7k
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
9159
27.7k
  if (r.ec == std::errc::result_out_of_range) {
9160
324
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
9161
324
    is_valid = false;
9162
324
    return 0;
9163
324
  }
9164
27.3k
  ada_log("parse_port: ", parsed_port);
9165
27.3k
  const size_t consumed = size_t(r.ptr - view.data());
9166
27.3k
  ada_log("parse_port: consumed ", consumed);
9167
27.3k
  if (check_trailing_content) {
9168
24.8k
    is_valid &=
9169
24.8k
        (consumed == view.size() || view[consumed] == '/' ||
9170
765
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
9171
24.8k
  }
9172
27.3k
  ada_log("parse_port: is_valid = ", is_valid);
9173
27.3k
  if (is_valid) {
9174
26.8k
    ada_log("parse_port", r.ec == std::errc());
9175
    // scheme_default_port can return 0, and we should allow 0 as a base port.
9176
26.8k
    auto default_port = scheme_default_port();
9177
26.8k
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
9178
26.7k
                         (default_port != parsed_port);
9179
26.8k
    if (r.ec == std::errc() && is_port_valid) {
9180
23.8k
      update_base_port(parsed_port);
9181
23.8k
    } else {
9182
3.03k
      clear_port();
9183
3.03k
    }
9184
26.8k
  }
9185
27.3k
  return consumed;
9186
27.7k
}
9187
9188
8.80k
constexpr void url_aggregator::set_protocol_as_file() {
9189
8.80k
  ada_log("url_aggregator::set_protocol_as_file ");
9190
8.80k
  ADA_ASSERT_TRUE(validate());
9191
8.80k
  type = ada::scheme::type::FILE;
9192
  // next line could overflow but unsigned arithmetic has well-defined
9193
  // overflows.
9194
8.80k
  uint32_t new_difference = 5 - components.protocol_end;
9195
9196
8.80k
  if (buffer.empty()) {
9197
1.99k
    buffer.append("file:");
9198
6.80k
  } else {
9199
6.80k
    buffer.erase(0, components.protocol_end);
9200
6.80k
    buffer.insert(0, "file:");
9201
6.80k
  }
9202
8.80k
  components.protocol_end = 5;
9203
9204
  // Update the rest of the components.
9205
8.80k
  components.username_end += new_difference;
9206
8.80k
  components.host_start += new_difference;
9207
8.80k
  components.host_end += new_difference;
9208
8.80k
  components.pathname_start += new_difference;
9209
8.80k
  if (components.search_start != url_components::omitted) {
9210
0
    components.search_start += new_difference;
9211
0
  }
9212
8.80k
  if (components.hash_start != url_components::omitted) {
9213
0
    components.hash_start += new_difference;
9214
0
  }
9215
8.80k
  ADA_ASSERT_TRUE(validate());
9216
8.80k
}
9217
9218
123k
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
9219
123k
  if (!is_valid) {
9220
0
    return true;
9221
0
  }
9222
123k
  if (!components.check_offset_consistency()) {
9223
0
    ada_log("url_aggregator::validate inconsistent components \n",
9224
0
            to_diagram());
9225
0
    return false;
9226
0
  }
9227
  // We have a credible components struct, but let us investivate more
9228
  // carefully:
9229
  /**
9230
   * https://user:pass@example.com:1234/foo/bar?baz#quux
9231
   *       |     |    |          | ^^^^|       |   |
9232
   *       |     |    |          | |   |       |   `----- hash_start
9233
   *       |     |    |          | |   |       `--------- search_start
9234
   *       |     |    |          | |   `----------------- pathname_start
9235
   *       |     |    |          | `--------------------- port
9236
   *       |     |    |          `----------------------- host_end
9237
   *       |     |    `---------------------------------- host_start
9238
   *       |     `--------------------------------------- username_end
9239
   *       `--------------------------------------------- protocol_end
9240
   */
9241
123k
  if (components.protocol_end == url_components::omitted) {
9242
0
    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
9243
0
    return false;
9244
0
  }
9245
123k
  if (components.username_end == url_components::omitted) {
9246
0
    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
9247
0
    return false;
9248
0
  }
9249
123k
  if (components.host_start == url_components::omitted) {
9250
0
    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
9251
0
    return false;
9252
0
  }
9253
123k
  if (components.host_end == url_components::omitted) {
9254
0
    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
9255
0
    return false;
9256
0
  }
9257
123k
  if (components.pathname_start == url_components::omitted) {
9258
0
    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
9259
0
    return false;
9260
0
  }
9261
9262
123k
  if (components.protocol_end > buffer.size()) {
9263
0
    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
9264
0
    return false;
9265
0
  }
9266
123k
  if (components.username_end > buffer.size()) {
9267
0
    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
9268
0
    return false;
9269
0
  }
9270
123k
  if (components.host_start > buffer.size()) {
9271
0
    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
9272
0
    return false;
9273
0
  }
9274
123k
  if (components.host_end > buffer.size()) {
9275
0
    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
9276
0
    return false;
9277
0
  }
9278
123k
  if (components.pathname_start > buffer.size()) {
9279
0
    ada_log("url_aggregator::validate pathname_start overflow \n",
9280
0
            to_diagram());
9281
0
    return false;
9282
0
  }
9283
9284
123k
  if (components.protocol_end > 0) {
9285
123k
    if (buffer[components.protocol_end - 1] != ':') {
9286
0
      ada_log(
9287
0
          "url_aggregator::validate missing : at the end of the protocol \n",
9288
0
          to_diagram());
9289
0
      return false;
9290
0
    }
9291
123k
  }
9292
9293
123k
  if (components.username_end != buffer.size() &&
9294
122k
      components.username_end > components.protocol_end + 2) {
9295
49.8k
    if (buffer[components.username_end] != ':' &&
9296
3.04k
        buffer[components.username_end] != '@') {
9297
0
      ada_log(
9298
0
          "url_aggregator::validate missing : or @ at the end of the username "
9299
0
          "\n",
9300
0
          to_diagram());
9301
0
      return false;
9302
0
    }
9303
49.8k
  }
9304
9305
123k
  if (components.host_start != buffer.size()) {
9306
122k
    if (components.host_start > components.username_end) {
9307
47.8k
      if (buffer[components.host_start] != '@') {
9308
0
        ada_log(
9309
0
            "url_aggregator::validate missing @ at the end of the password \n",
9310
0
            to_diagram());
9311
0
        return false;
9312
0
      }
9313
74.5k
    } else if (components.host_start == components.username_end &&
9314
74.5k
               components.host_end > components.host_start) {
9315
64.2k
      if (components.host_start == components.protocol_end + 2) {
9316
61.1k
        if (buffer[components.protocol_end] != '/' ||
9317
61.1k
            buffer[components.protocol_end + 1] != '/') {
9318
0
          ada_log(
9319
0
              "url_aggregator::validate missing // between protocol and host "
9320
0
              "\n",
9321
0
              to_diagram());
9322
0
          return false;
9323
0
        }
9324
61.1k
      } else {
9325
3.04k
        if (components.host_start > components.protocol_end &&
9326
3.04k
            buffer[components.host_start] != '@') {
9327
0
          ada_log(
9328
0
              "url_aggregator::validate missing @ at the end of the username "
9329
0
              "\n",
9330
0
              to_diagram());
9331
0
          return false;
9332
0
        }
9333
3.04k
      }
9334
64.2k
    } else {
9335
10.2k
      if (components.host_end != components.host_start) {
9336
0
        ada_log("url_aggregator::validate expected omitted host \n",
9337
0
                to_diagram());
9338
0
        return false;
9339
0
      }
9340
10.2k
    }
9341
122k
  }
9342
123k
  if (components.host_end != buffer.size() &&
9343
121k
      components.pathname_start > components.host_end) {
9344
38.1k
    if (components.pathname_start == components.host_end + 2 &&
9345
3.15k
        buffer[components.host_end] == '/' &&
9346
109
        buffer[components.host_end + 1] == '.') {
9347
109
      if (components.pathname_start + 1 >= buffer.size() ||
9348
109
          buffer[components.pathname_start] != '/' ||
9349
109
          buffer[components.pathname_start + 1] != '/') {
9350
0
        ada_log(
9351
0
            "url_aggregator::validate expected the path to begin with // \n",
9352
0
            to_diagram());
9353
0
        return false;
9354
0
      }
9355
38.0k
    } else if (buffer[components.host_end] != ':') {
9356
0
      ada_log("url_aggregator::validate missing : at the port \n",
9357
0
              to_diagram());
9358
0
      return false;
9359
0
    }
9360
38.1k
  }
9361
123k
  if (components.pathname_start != buffer.size() &&
9362
121k
      components.pathname_start < components.search_start &&
9363
121k
      components.pathname_start < components.hash_start && !has_opaque_path) {
9364
115k
    if (buffer[components.pathname_start] != '/') {
9365
0
      ada_log("url_aggregator::validate missing / at the path \n",
9366
0
              to_diagram());
9367
0
      return false;
9368
0
    }
9369
115k
  }
9370
123k
  if (components.search_start != url_components::omitted) {
9371
84.2k
    if (buffer[components.search_start] != '?') {
9372
0
      ada_log("url_aggregator::validate missing ? at the search \n",
9373
0
              to_diagram());
9374
0
      return false;
9375
0
    }
9376
84.2k
  }
9377
123k
  if (components.hash_start != url_components::omitted) {
9378
61.3k
    if (buffer[components.hash_start] != '#') {
9379
0
      ada_log("url_aggregator::validate missing # at the hash \n",
9380
0
              to_diagram());
9381
0
      return false;
9382
0
    }
9383
61.3k
  }
9384
9385
123k
  return true;
9386
123k
}
9387
9388
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() const
9389
187k
    ada_lifetime_bound {
9390
187k
  ada_log("url_aggregator::get_pathname pathname_start = ",
9391
187k
          components.pathname_start, " buffer.size() = ", buffer.size(),
9392
187k
          " components.search_start = ", components.search_start,
9393
187k
          " components.hash_start = ", components.hash_start);
9394
187k
  auto ending_index = uint32_t(buffer.size());
9395
187k
  if (components.search_start != url_components::omitted) {
9396
56.2k
    ending_index = components.search_start;
9397
131k
  } else if (components.hash_start != url_components::omitted) {
9398
2.63k
    ending_index = components.hash_start;
9399
2.63k
  }
9400
187k
  return helpers::substring(buffer, components.pathname_start, ending_index);
9401
187k
}
9402
9403
inline std::ostream& operator<<(std::ostream& out,
9404
0
                                const ada::url_aggregator& u) {
9405
0
  return out << u.to_string();
9406
0
}
9407
9408
15.1k
void url_aggregator::update_host_to_base_host(const std::string_view input) {
9409
15.1k
  ada_log("url_aggregator::update_host_to_base_host ", input);
9410
15.1k
  ADA_ASSERT_TRUE(validate());
9411
15.1k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
9412
15.1k
  if (type != ada::scheme::type::FILE) {
9413
    // Let host be the result of host parsing host_view with url is not special.
9414
13.2k
    if (input.empty() && !is_special()) {
9415
1.00k
      if (has_hostname()) {
9416
74
        clear_hostname();
9417
928
      } else if (has_dash_dot()) {
9418
0
        add_authority_slashes_if_needed();
9419
0
        delete_dash_dot();
9420
0
      }
9421
1.00k
      return;
9422
1.00k
    }
9423
13.2k
  }
9424
14.1k
  update_base_hostname(input);
9425
14.1k
  ADA_ASSERT_TRUE(validate());
9426
14.1k
  return;
9427
15.1k
}
9428
}  // namespace ada
9429
9430
#endif  // ADA_URL_AGGREGATOR_INL_H
9431
/* end file include/ada/url_aggregator-inl.h */
9432
/* begin file include/ada/url_search_params.h */
9433
/**
9434
 * @file url_search_params.h
9435
 * @brief URL query string parameter manipulation.
9436
 *
9437
 * This file provides the `url_search_params` class for parsing, manipulating,
9438
 * and serializing URL query strings. It implements the URLSearchParams API
9439
 * from the WHATWG URL Standard.
9440
 *
9441
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9442
 */
9443
#ifndef ADA_URL_SEARCH_PARAMS_H
9444
#define ADA_URL_SEARCH_PARAMS_H
9445
9446
#include <optional>
9447
#include <string>
9448
#include <string_view>
9449
#include <vector>
9450
9451
namespace ada {
9452
9453
/**
9454
 * @brief Iterator types for url_search_params iteration.
9455
 */
9456
enum class url_search_params_iter_type {
9457
  KEYS,    /**< Iterate over parameter keys only */
9458
  VALUES,  /**< Iterate over parameter values only */
9459
  ENTRIES, /**< Iterate over key-value pairs */
9460
};
9461
9462
template <typename T, url_search_params_iter_type Type>
9463
struct url_search_params_iter;
9464
9465
/** Type alias for a key-value pair of string views. */
9466
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
9467
9468
/** Iterator over search parameter keys. */
9469
using url_search_params_keys_iter =
9470
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
9471
/** Iterator over search parameter values. */
9472
using url_search_params_values_iter =
9473
    url_search_params_iter<std::string_view,
9474
                           url_search_params_iter_type::VALUES>;
9475
/** Iterator over search parameter key-value pairs. */
9476
using url_search_params_entries_iter =
9477
    url_search_params_iter<key_value_view_pair,
9478
                           url_search_params_iter_type::ENTRIES>;
9479
9480
/**
9481
 * @brief Class for parsing and manipulating URL query strings.
9482
 *
9483
 * The `url_search_params` class provides methods to parse, modify, and
9484
 * serialize URL query parameters (the part after '?' in a URL). It handles
9485
 * percent-encoding and decoding automatically.
9486
 *
9487
 * All string inputs must be valid UTF-8. The caller is responsible for
9488
 * ensuring UTF-8 validity.
9489
 *
9490
 * Construction and `reset` refuse query strings longer than
9491
 * `get_max_input_length()` (the object is left empty). Individual `append` /
9492
 * `set` calls are not length-capped.
9493
 *
9494
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9495
 */
9496
struct url_search_params {
9497
  url_search_params() = default;
9498
9499
  /**
9500
   * Constructs url_search_params by parsing a query string.
9501
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9502
   *        If longer than `get_max_input_length()`, the object stays empty.
9503
   */
9504
39.8k
  explicit url_search_params(const std::string_view input) {
9505
39.8k
    initialize(input);
9506
39.8k
  }
9507
9508
  url_search_params(const url_search_params& u) = default;
9509
0
  url_search_params(url_search_params&& u) noexcept = default;
9510
  url_search_params& operator=(url_search_params&& u) noexcept = default;
9511
  url_search_params& operator=(const url_search_params& u) = default;
9512
39.8k
  ~url_search_params() = default;
9513
9514
  /**
9515
   * Returns the number of key-value pairs.
9516
   * @return The total count of parameters.
9517
   */
9518
  [[nodiscard]] inline size_t size() const noexcept;
9519
9520
  /**
9521
   * Appends a new key-value pair to the parameter list.
9522
   * @param key The parameter name (must be valid UTF-8).
9523
   * @param value The parameter value (must be valid UTF-8).
9524
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9525
   */
9526
  inline void append(std::string_view key, std::string_view value);
9527
9528
  /**
9529
   * Removes all pairs with the given key.
9530
   * @param key The parameter name to remove.
9531
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9532
   */
9533
  inline void remove(std::string_view key);
9534
9535
  /**
9536
   * Removes all pairs with the given key and value.
9537
   * @param key The parameter name.
9538
   * @param value The parameter value to match.
9539
   */
9540
  inline void remove(std::string_view key, std::string_view value);
9541
9542
  /**
9543
   * Returns the value of the first pair with the given key.
9544
   * @param key The parameter name to search for.
9545
   * @return The value if found, or std::nullopt if not present.
9546
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9547
   */
9548
  inline std::optional<std::string_view> get(std::string_view key);
9549
9550
  /**
9551
   * Returns all values for pairs with the given key.
9552
   * @param key The parameter name to search for.
9553
   * @return A vector of all matching values (may be empty).
9554
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9555
   */
9556
  inline std::vector<std::string> get_all(std::string_view key);
9557
9558
  /**
9559
   * Checks if any pair has the given key.
9560
   * @param key The parameter name to search for.
9561
   * @return `true` if at least one pair has this key.
9562
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9563
   */
9564
  inline bool has(std::string_view key) noexcept;
9565
9566
  /**
9567
   * Checks if any pair matches the given key and value.
9568
   * @param key The parameter name to search for.
9569
   * @param value The parameter value to match.
9570
   * @return `true` if a matching pair exists.
9571
   */
9572
  inline bool has(std::string_view key, std::string_view value) noexcept;
9573
9574
  /**
9575
   * Sets a parameter value, replacing any existing pairs with the same key.
9576
   * @param key The parameter name (must be valid UTF-8).
9577
   * @param value The parameter value (must be valid UTF-8).
9578
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9579
   */
9580
  inline void set(std::string_view key, std::string_view value);
9581
9582
  /**
9583
   * Sorts all key-value pairs by their keys using code unit comparison.
9584
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9585
   */
9586
  inline void sort();
9587
9588
  /**
9589
   * Serializes the parameters to a query string (without leading '?').
9590
   * @return The percent-encoded query string.
9591
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9592
   */
9593
  inline std::string to_string() const;
9594
9595
  /**
9596
   * Returns an iterator over all parameter keys.
9597
   * Keys may repeat if there are duplicate parameters.
9598
   * @return An iterator yielding string_view keys.
9599
   * @note The iterator is invalidated if this object is modified.
9600
   */
9601
  inline url_search_params_keys_iter get_keys();
9602
9603
  /**
9604
   * Returns an iterator over all parameter values.
9605
   * @return An iterator yielding string_view values.
9606
   * @note The iterator is invalidated if this object is modified.
9607
   */
9608
  inline url_search_params_values_iter get_values();
9609
9610
  /**
9611
   * Returns an iterator over all key-value pairs.
9612
   * @return An iterator yielding key-value pair views.
9613
   * @note The iterator is invalidated if this object is modified.
9614
   */
9615
  inline url_search_params_entries_iter get_entries();
9616
9617
  /**
9618
   * C++ style conventional iterator support. const only because we
9619
   * do not really want the params to be modified via the iterator.
9620
   */
9621
0
  inline auto begin() const { return params.begin(); }
9622
0
  inline auto end() const { return params.end(); }
9623
0
  inline auto front() const { return params.front(); }
9624
0
  inline auto back() const { return params.back(); }
9625
0
  inline auto operator[](size_t index) const { return params[index]; }
9626
9627
  /**
9628
   * @private
9629
   * Used to reset the search params to a new input.
9630
   * Used primarily for C API.
9631
   * @param input
9632
   */
9633
  void reset(std::string_view input);
9634
9635
 private:
9636
  typedef std::pair<std::string, std::string> key_value_pair;
9637
  std::vector<key_value_pair> params{};
9638
9639
  /**
9640
   * The init parameter must be valid UTF-8.
9641
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9642
   */
9643
  void initialize(std::string_view init);
9644
9645
  template <typename T, url_search_params_iter_type Type>
9646
  friend struct url_search_params_iter;
9647
};  // url_search_params
9648
9649
/**
9650
 * @brief JavaScript-style iterator for url_search_params.
9651
 *
9652
 * Provides a `next()` method that returns successive values until exhausted.
9653
 * This matches the iterator pattern used in the Web Platform.
9654
 *
9655
 * @tparam T The type of value returned by the iterator.
9656
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9657
 *
9658
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9659
 */
9660
template <typename T, url_search_params_iter_type Type>
9661
struct url_search_params_iter {
9662
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()
9663
  url_search_params_iter(const url_search_params_iter& u) = default;
9664
  url_search_params_iter(url_search_params_iter&& u) noexcept = default;
9665
  url_search_params_iter& operator=(url_search_params_iter&& u) noexcept =
9666
      default;
9667
  url_search_params_iter& operator=(const url_search_params_iter& u) = default;
9668
  ~url_search_params_iter() = default;
9669
9670
  /**
9671
   * Returns the next value in the iteration sequence.
9672
   * @return The next value, or std::nullopt if iteration is complete.
9673
   */
9674
  inline std::optional<T> next();
9675
9676
  /**
9677
   * Checks if more values are available.
9678
   * @return `true` if `next()` will return a value, `false` if exhausted.
9679
   */
9680
  inline bool has_next() const;
9681
9682
 private:
9683
  static url_search_params EMPTY;
9684
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&)
9685
9686
  url_search_params& params;
9687
  size_t pos = 0;
9688
9689
  friend struct url_search_params;
9690
};
9691
9692
}  // namespace ada
9693
#endif
9694
/* end file include/ada/url_search_params.h */
9695
/* begin file include/ada/url_search_params-inl.h */
9696
/**
9697
 * @file url_search_params-inl.h
9698
 * @brief Inline declarations for the URL Search Params
9699
 */
9700
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9701
#define ADA_URL_SEARCH_PARAMS_INL_H
9702
9703
9704
#include <algorithm>
9705
#include <cstdint>
9706
#include <optional>
9707
#include <ranges>
9708
#include <string>
9709
#include <string_view>
9710
#include <vector>
9711
9712
namespace ada {
9713
9714
// Declared in implementation.h; used here as a DoS bound on untrusted query
9715
// strings (ada.h includes both headers).
9716
uint32_t get_max_input_length();
9717
9718
// A default, empty url_search_params for use with empty iterators.
9719
template <typename T, ada::url_search_params_iter_type Type>
9720
url_search_params url_search_params_iter<T, Type>::EMPTY;
9721
9722
0
inline void url_search_params::reset(std::string_view input) {
9723
0
  params.clear();
9724
0
  initialize(input);
9725
0
}
9726
9727
39.8k
inline void url_search_params::initialize(std::string_view input) {
9728
39.8k
  if (!input.empty() && input.front() == '?') {
9729
131
    input.remove_prefix(1);
9730
131
  }
9731
39.8k
  if (input.empty()) {
9732
3.76k
    return;
9733
3.76k
  }
9734
  // Refuse overlong query strings (same process-wide cap as URL parsing).
9735
36.1k
  if (input.size() > get_max_input_length()) {
9736
0
    return;
9737
0
  }
9738
9739
36.1k
  params.reserve(size_t(std::count(input.begin(), input.end(), '&')) + 1);
9740
9741
56.3k
  auto process_key_value = [&](const std::string_view current) {
9742
56.3k
    const auto equal = current.find('=');
9743
56.3k
    if (equal == std::string_view::npos) {
9744
17.2k
      params.emplace_back(unicode::form_urlencoded_decode(current), "");
9745
39.1k
    } else {
9746
39.1k
      params.emplace_back(
9747
39.1k
          unicode::form_urlencoded_decode(current.substr(0, equal)),
9748
39.1k
          unicode::form_urlencoded_decode(current.substr(equal + 1)));
9749
39.1k
    }
9750
56.3k
  };
9751
9752
57.1k
  while (!input.empty()) {
9753
57.0k
    const auto ampersand_index = input.find('&');
9754
9755
57.0k
    if (ampersand_index == std::string_view::npos) {
9756
36.0k
      if (!input.empty()) {
9757
36.0k
        process_key_value(input);
9758
36.0k
      }
9759
36.0k
      break;
9760
36.0k
    } else if (ampersand_index != 0) {
9761
20.3k
      process_key_value(input.substr(0, ampersand_index));
9762
20.3k
    }
9763
9764
21.0k
    input.remove_prefix(ampersand_index + 1);
9765
21.0k
  }
9766
36.1k
}
9767
9768
inline void url_search_params::append(const std::string_view key,
9769
19.9k
                                      const std::string_view value) {
9770
19.9k
  params.emplace_back(key, value);
9771
19.9k
}
9772
9773
0
inline size_t url_search_params::size() const noexcept { return params.size(); }
9774
9775
inline std::optional<std::string_view> url_search_params::get(
9776
0
    const std::string_view key) {
9777
0
  auto entry = std::ranges::find_if(
9778
0
      params, [&key](const auto& param) { return param.first == key; });
9779
9780
0
  if (entry == params.end()) {
9781
0
    return std::nullopt;
9782
0
  }
9783
9784
0
  return entry->second;
9785
0
}
9786
9787
inline std::vector<std::string> url_search_params::get_all(
9788
0
    const std::string_view key) {
9789
0
  std::vector<std::string> out{};
9790
9791
0
  for (auto& param : params) {
9792
0
    if (param.first == key) {
9793
0
      out.emplace_back(param.second);
9794
0
    }
9795
0
  }
9796
9797
0
  return out;
9798
0
}
9799
9800
0
inline bool url_search_params::has(const std::string_view key) noexcept {
9801
0
  auto entry = std::ranges::find_if(
9802
0
      params, [&key](const auto& param) { return param.first == key; });
9803
0
  return entry != params.end();
9804
0
}
9805
9806
inline bool url_search_params::has(std::string_view key,
9807
0
                                   std::string_view value) noexcept {
9808
0
  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
9809
0
    return param.first == key && param.second == value;
9810
0
  });
9811
0
  return entry != params.end();
9812
0
}
9813
9814
39.8k
inline std::string url_search_params::to_string() const {
9815
39.8k
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9816
39.8k
  std::string out{};
9817
116k
  for (size_t i = 0; i < params.size(); i++) {
9818
76.3k
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9819
76.3k
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9820
9821
    // Performance optimization: Move this inside percent_encode.
9822
76.3k
    std::ranges::replace(key, ' ', '+');
9823
76.3k
    std::ranges::replace(value, ' ', '+');
9824
9825
76.3k
    if (i != 0) {
9826
36.4k
      out += "&";
9827
36.4k
    }
9828
76.3k
    out.append(key);
9829
76.3k
    out += "=";
9830
76.3k
    out.append(value);
9831
76.3k
  }
9832
39.8k
  return out;
9833
39.8k
}
9834
9835
inline void url_search_params::set(const std::string_view key,
9836
0
                                   const std::string_view value) {
9837
0
  const auto find = [&key](const auto& param) { return param.first == key; };
9838
9839
0
  auto it = std::ranges::find_if(params, find);
9840
9841
0
  if (it == params.end()) {
9842
0
    params.emplace_back(key, value);
9843
0
  } else {
9844
0
    it->second = value;
9845
0
    params.erase(std::remove_if(std::next(it), params.end(), find),
9846
0
                 params.end());
9847
0
  }
9848
0
}
9849
9850
0
inline void url_search_params::remove(const std::string_view key) {
9851
0
  std::erase_if(params,
9852
0
                [&key](const auto& param) { return param.first == key; });
9853
0
}
9854
9855
inline void url_search_params::remove(const std::string_view key,
9856
0
                                      const std::string_view value) {
9857
0
  std::erase_if(params, [&key, &value](const auto& param) {
9858
0
    return param.first == key && param.second == value;
9859
0
  });
9860
0
}
9861
9862
0
inline void url_search_params::sort() {
9863
  // Keys are expected to be valid UTF-8, but percent_decode can produce
9864
  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
9865
0
  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
9866
0
                                      const key_value_pair& rhs) {
9867
0
    size_t i = 0, j = 0;
9868
0
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9869
0
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9870
0
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9871
0
      uint32_t codePoint1 = 0, codePoint2 = 0;
9872
9873
0
      if (low_surrogate1 != 0) {
9874
0
        codePoint1 = low_surrogate1;
9875
0
        low_surrogate1 = 0;
9876
0
      } else {
9877
0
        uint8_t c1 = uint8_t(lhs.first[i]);
9878
0
        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
9879
0
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9880
0
          i += 2;
9881
0
        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
9882
0
          codePoint1 = ((c1 & 0x0F) << 12) |
9883
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9884
0
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9885
0
          i += 3;
9886
0
        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
9887
0
          codePoint1 = ((c1 & 0x07) << 18) |
9888
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9889
0
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9890
0
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9891
0
          i += 4;
9892
9893
0
          codePoint1 -= 0x10000;
9894
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9895
0
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9896
0
          codePoint1 = high_surrogate;
9897
0
        } else {
9898
          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
9899
0
          codePoint1 = c1;
9900
0
          i++;
9901
0
        }
9902
0
      }
9903
9904
0
      if (low_surrogate2 != 0) {
9905
0
        codePoint2 = low_surrogate2;
9906
0
        low_surrogate2 = 0;
9907
0
      } else {
9908
0
        uint8_t c2 = uint8_t(rhs.first[j]);
9909
0
        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
9910
0
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
9911
0
          j += 2;
9912
0
        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
9913
0
          codePoint2 = ((c2 & 0x0F) << 12) |
9914
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
9915
0
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
9916
0
          j += 3;
9917
0
        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
9918
0
          codePoint2 = ((c2 & 0x07) << 18) |
9919
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
9920
0
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
9921
0
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
9922
0
          j += 4;
9923
0
          codePoint2 -= 0x10000;
9924
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
9925
0
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
9926
0
          codePoint2 = high_surrogate;
9927
0
        } else {
9928
          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
9929
0
          codePoint2 = c2;
9930
0
          j++;
9931
0
        }
9932
0
      }
9933
9934
0
      if (codePoint1 != codePoint2) {
9935
0
        return (codePoint1 < codePoint2);
9936
0
      }
9937
0
    }
9938
0
    return (j < rhs.first.size() || low_surrogate2 != 0);
9939
0
  });
9940
0
}
9941
9942
0
inline url_search_params_keys_iter url_search_params::get_keys() {
9943
0
  return url_search_params_keys_iter(*this);
9944
0
}
9945
9946
/**
9947
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9948
 */
9949
0
inline url_search_params_values_iter url_search_params::get_values() {
9950
0
  return url_search_params_values_iter(*this);
9951
0
}
9952
9953
/**
9954
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9955
 */
9956
0
inline url_search_params_entries_iter url_search_params::get_entries() {
9957
0
  return url_search_params_entries_iter(*this);
9958
0
}
9959
9960
template <typename T, url_search_params_iter_type Type>
9961
0
inline bool url_search_params_iter<T, Type>::has_next() const {
9962
0
  return pos < params.params.size();
9963
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
9964
9965
template <>
9966
0
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
9967
0
  if (!has_next()) {
9968
0
    return std::nullopt;
9969
0
  }
9970
0
  return params.params[pos++].first;
9971
0
}
9972
9973
template <>
9974
0
inline std::optional<std::string_view> url_search_params_values_iter::next() {
9975
0
  if (!has_next()) {
9976
0
    return std::nullopt;
9977
0
  }
9978
0
  return params.params[pos++].second;
9979
0
}
9980
9981
template <>
9982
inline std::optional<key_value_view_pair>
9983
0
url_search_params_entries_iter::next() {
9984
0
  if (!has_next()) {
9985
0
    return std::nullopt;
9986
0
  }
9987
0
  return params.params[pos++];
9988
0
}
9989
9990
}  // namespace ada
9991
9992
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
9993
/* end file include/ada/url_search_params-inl.h */
9994
9995
/* begin file include/ada/url_pattern-inl.h */
9996
/**
9997
 * @file url_pattern-inl.h
9998
 * @brief Declaration for the URLPattern inline functions.
9999
 */
10000
#ifndef ADA_URL_PATTERN_INL_H
10001
#define ADA_URL_PATTERN_INL_H
10002
10003
10004
#include <algorithm>
10005
#include <string_view>
10006
#include <utility>
10007
10008
#if ADA_INCLUDE_URL_PATTERN
10009
namespace ada {
10010
10011
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
10012
0
  return protocol == other.protocol && username == other.username &&
10013
0
         password == other.password && hostname == other.hostname &&
10014
0
         port == other.port && search == other.search && hash == other.hash &&
10015
0
         pathname == other.pathname;
10016
0
}
10017
10018
inline bool url_pattern_component_result::operator==(
10019
0
    const url_pattern_component_result& other) const {
10020
0
  return input == other.input && groups == other.groups;
10021
0
}
10022
10023
template <url_pattern_regex::regex_concept regex_provider>
10024
url_pattern_component_result
10025
url_pattern_component<regex_provider>::create_component_match_result(
10026
    std::string&& input,
10027
    std::vector<std::optional<std::string>>&& exec_result) {
10028
  // Let result be a new URLPatternComponentResult.
10029
  // Set result["input"] to input.
10030
  // Let groups be a record<USVString, (USVString or undefined)>.
10031
  auto result =
10032
      url_pattern_component_result{.input = std::move(input), .groups = {}};
10033
10034
  // We explicitly start iterating from 0 even though the spec
10035
  // says we should start from 1. This case is handled by the
10036
  // std_regex_provider which removes the full match from index 0.
10037
  // Use min() to guard against potential mismatches between
10038
  // exec_result size and group_name_list size.
10039
  const size_t size = std::min(exec_result.size(), group_name_list.size());
10040
  result.groups.reserve(size);
10041
  for (size_t index = 0; index < size; index++) {
10042
    result.groups.emplace(group_name_list[index],
10043
                          std::move(exec_result[index]));
10044
  }
10045
  return result;
10046
}
10047
10048
template <url_pattern_regex::regex_concept regex_provider>
10049
std::string_view url_pattern<regex_provider>::get_protocol() const
10050
    ada_lifetime_bound {
10051
  // Return this's associated URL pattern's protocol component's pattern string.
10052
  return protocol_component.pattern;
10053
}
10054
template <url_pattern_regex::regex_concept regex_provider>
10055
std::string_view url_pattern<regex_provider>::get_username() const
10056
    ada_lifetime_bound {
10057
  // Return this's associated URL pattern's username component's pattern string.
10058
  return username_component.pattern;
10059
}
10060
template <url_pattern_regex::regex_concept regex_provider>
10061
std::string_view url_pattern<regex_provider>::get_password() const
10062
    ada_lifetime_bound {
10063
  // Return this's associated URL pattern's password component's pattern string.
10064
  return password_component.pattern;
10065
}
10066
template <url_pattern_regex::regex_concept regex_provider>
10067
std::string_view url_pattern<regex_provider>::get_hostname() const
10068
    ada_lifetime_bound {
10069
  // Return this's associated URL pattern's hostname component's pattern string.
10070
  return hostname_component.pattern;
10071
}
10072
template <url_pattern_regex::regex_concept regex_provider>
10073
std::string_view url_pattern<regex_provider>::get_port() const
10074
    ada_lifetime_bound {
10075
  // Return this's associated URL pattern's port component's pattern string.
10076
  return port_component.pattern;
10077
}
10078
template <url_pattern_regex::regex_concept regex_provider>
10079
std::string_view url_pattern<regex_provider>::get_pathname() const
10080
    ada_lifetime_bound {
10081
  // Return this's associated URL pattern's pathname component's pattern string.
10082
  return pathname_component.pattern;
10083
}
10084
template <url_pattern_regex::regex_concept regex_provider>
10085
std::string_view url_pattern<regex_provider>::get_search() const
10086
    ada_lifetime_bound {
10087
  // Return this's associated URL pattern's search component's pattern string.
10088
  return search_component.pattern;
10089
}
10090
template <url_pattern_regex::regex_concept regex_provider>
10091
std::string_view url_pattern<regex_provider>::get_hash() const
10092
    ada_lifetime_bound {
10093
  // Return this's associated URL pattern's hash component's pattern string.
10094
  return hash_component.pattern;
10095
}
10096
template <url_pattern_regex::regex_concept regex_provider>
10097
bool url_pattern<regex_provider>::ignore_case() const {
10098
  return ignore_case_;
10099
}
10100
template <url_pattern_regex::regex_concept regex_provider>
10101
bool url_pattern<regex_provider>::has_regexp_groups() const {
10102
  // If this's associated URL pattern's has regexp groups, then return true.
10103
  return protocol_component.has_regexp_groups ||
10104
         username_component.has_regexp_groups ||
10105
         password_component.has_regexp_groups ||
10106
         hostname_component.has_regexp_groups ||
10107
         port_component.has_regexp_groups ||
10108
         pathname_component.has_regexp_groups ||
10109
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
10110
}
10111
10112
0
inline bool url_pattern_part::is_regexp() const noexcept {
10113
0
  return type == url_pattern_part_type::REGEXP;
10114
0
}
10115
10116
inline std::string_view url_pattern_compile_component_options::get_delimiter()
10117
0
    const {
10118
0
  if (delimiter) {
10119
0
    return {&delimiter.value(), 1};
10120
0
  }
10121
0
  return {};
10122
0
}
10123
10124
inline std::string_view url_pattern_compile_component_options::get_prefix()
10125
0
    const {
10126
0
  if (prefix) {
10127
0
    return {&prefix.value(), 1};
10128
0
  }
10129
0
  return {};
10130
0
}
10131
10132
template <url_pattern_regex::regex_concept regex_provider>
10133
template <url_pattern_encoding_callback F>
10134
tl::expected<url_pattern_component<regex_provider>, errors>
10135
url_pattern_component<regex_provider>::compile(
10136
    std::string_view input, F& encoding_callback,
10137
    url_pattern_compile_component_options& options) {
10138
  ada_log("url_pattern_component::compile input: ", input);
10139
  // Let part list be the result of running parse a pattern string given input,
10140
  // options, and encoding callback.
10141
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
10142
                                                             encoding_callback);
10143
10144
  if (!part_list) {
10145
    ada_log("parse_pattern_string failed");
10146
    return tl::unexpected(part_list.error());
10147
  }
10148
10149
  // Detect pattern type early to potentially skip expensive regex compilation
10150
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
10151
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
10152
10153
  url_pattern_component_type component_type =
10154
      url_pattern_component_type::REGEXP;
10155
  std::string exact_match_value{};
10156
10157
  if (part_list->empty()) {
10158
    component_type = url_pattern_component_type::EMPTY;
10159
  } else if (part_list->size() == 1) {
10160
    const auto& part = (*part_list)[0];
10161
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
10162
        part.modifier == url_pattern_part_modifier::none &&
10163
        !options.ignore_case) {
10164
      component_type = url_pattern_component_type::EXACT_MATCH;
10165
      exact_match_value = part.value;
10166
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
10167
               part.modifier == url_pattern_part_modifier::none &&
10168
               part.prefix.empty() && part.suffix.empty()) {
10169
      component_type = url_pattern_component_type::FULL_WILDCARD;
10170
    }
10171
  }
10172
10173
  // For simple patterns, skip regex generation and compilation entirely
10174
  if (component_type != url_pattern_component_type::REGEXP) {
10175
    auto pattern_string =
10176
        url_pattern_helpers::generate_pattern_string(*part_list, options);
10177
    // For FULL_WILDCARD, we need the group name from
10178
    // generate_regular_expression
10179
    std::vector<std::string> name_list;
10180
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
10181
        !part_list->empty()) {
10182
      name_list.push_back((*part_list)[0].name);
10183
    }
10184
    return url_pattern_component<regex_provider>(
10185
        std::move(pattern_string), typename regex_provider::regex_type{},
10186
        std::move(name_list), has_regexp_groups, component_type,
10187
        std::move(exact_match_value));
10188
  }
10189
10190
  // Generate regex for complex patterns
10191
  auto [regular_expression_string, name_list] =
10192
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
10193
                                                                     options);
10194
  auto pattern_string =
10195
      url_pattern_helpers::generate_pattern_string(*part_list, options);
10196
10197
  std::optional<typename regex_provider::regex_type> regular_expression =
10198
      regex_provider::create_instance(regular_expression_string,
10199
                                      options.ignore_case);
10200
  if (!regular_expression) {
10201
    return tl::unexpected(errors::type_error);
10202
  }
10203
10204
  return url_pattern_component<regex_provider>(
10205
      std::move(pattern_string), std::move(*regular_expression),
10206
      std::move(name_list), has_regexp_groups, component_type,
10207
      std::move(exact_match_value));
10208
}
10209
10210
template <url_pattern_regex::regex_concept regex_provider>
10211
bool url_pattern_component<regex_provider>::fast_test(
10212
    std::string_view input) const noexcept {
10213
  // Fast path for simple patterns - avoid regex evaluation
10214
  // Using if-else for better branch prediction on common cases
10215
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10216
    return true;
10217
  }
10218
  if (type == url_pattern_component_type::EXACT_MATCH) {
10219
    return input == exact_match_value;
10220
  }
10221
  if (type == url_pattern_component_type::EMPTY) {
10222
    return input.empty();
10223
  }
10224
  // type == REGEXP
10225
  return regex_provider::regex_match(input, regexp);
10226
}
10227
10228
template <url_pattern_regex::regex_concept regex_provider>
10229
std::optional<std::vector<std::optional<std::string>>>
10230
url_pattern_component<regex_provider>::fast_match(
10231
    std::string_view input) const {
10232
  // Handle each type directly without redundant checks
10233
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10234
    // FULL_WILDCARD always matches - capture the input (even if empty)
10235
    // If there's no group name, return empty groups
10236
    if (group_name_list.empty()) {
10237
      return std::vector<std::optional<std::string>>{};
10238
    }
10239
    // Capture the matched input (including empty strings)
10240
    return std::vector<std::optional<std::string>>{std::string(input)};
10241
  }
10242
  if (type == url_pattern_component_type::EXACT_MATCH) {
10243
    if (input == exact_match_value) {
10244
      return std::vector<std::optional<std::string>>{};
10245
    }
10246
    return std::nullopt;
10247
  }
10248
  if (type == url_pattern_component_type::EMPTY) {
10249
    if (input.empty()) {
10250
      return std::vector<std::optional<std::string>>{};
10251
    }
10252
    return std::nullopt;
10253
  }
10254
  // type == REGEXP - use regex
10255
  return regex_provider::regex_search(input, regexp);
10256
}
10257
10258
template <url_pattern_regex::regex_concept regex_provider>
10259
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::exec(
10260
    const url_pattern_input& input, const std::string_view* base_url) {
10261
  // Return the result of match given this's associated URL pattern, input, and
10262
  // baseURL if given.
10263
  return match(input, base_url);
10264
}
10265
10266
template <url_pattern_regex::regex_concept regex_provider>
10267
bool url_pattern<regex_provider>::test_components(
10268
    std::string_view protocol, std::string_view username,
10269
    std::string_view password, std::string_view hostname, std::string_view port,
10270
    std::string_view pathname, std::string_view search,
10271
    std::string_view hash) const {
10272
  return protocol_component.fast_test(protocol) &&
10273
         username_component.fast_test(username) &&
10274
         password_component.fast_test(password) &&
10275
         hostname_component.fast_test(hostname) &&
10276
         port_component.fast_test(port) &&
10277
         pathname_component.fast_test(pathname) &&
10278
         search_component.fast_test(search) && hash_component.fast_test(hash);
10279
}
10280
10281
template <url_pattern_regex::regex_concept regex_provider>
10282
result<bool> url_pattern<regex_provider>::test(
10283
    const url_pattern_input& input, const std::string_view* base_url_string) {
10284
  // If input is a URLPatternInit
10285
  if (std::holds_alternative<url_pattern_init>(input)) {
10286
    if (base_url_string) {
10287
      return tl::unexpected(errors::type_error);
10288
    }
10289
10290
    std::string protocol{}, username{}, password{}, hostname{};
10291
    std::string port{}, pathname{}, search{}, hash{};
10292
10293
    auto apply_result = url_pattern_init::process(
10294
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10295
        protocol, username, password, hostname, port, pathname, search, hash);
10296
10297
    if (!apply_result) {
10298
      return false;
10299
    }
10300
10301
    std::string_view search_view = *apply_result->search;
10302
    if (search_view.starts_with("?")) {
10303
      search_view.remove_prefix(1);
10304
    }
10305
10306
    return test_components(*apply_result->protocol, *apply_result->username,
10307
                           *apply_result->password, *apply_result->hostname,
10308
                           *apply_result->port, *apply_result->pathname,
10309
                           search_view, *apply_result->hash);
10310
  }
10311
10312
  // URL string input path
10313
  result<url_aggregator> base_url;
10314
  if (base_url_string) {
10315
    base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10316
    if (!base_url) {
10317
      return false;
10318
    }
10319
  }
10320
10321
  auto url =
10322
      ada::parse<url_aggregator>(std::get<std::string_view>(input),
10323
                                 base_url.has_value() ? &*base_url : nullptr);
10324
  if (!url) {
10325
    return false;
10326
  }
10327
10328
  // Extract components as string_view
10329
  auto protocol_view = url->get_protocol();
10330
  if (protocol_view.ends_with(":")) {
10331
    protocol_view.remove_suffix(1);
10332
  }
10333
10334
  auto search_view = url->get_search();
10335
  if (search_view.starts_with("?")) {
10336
    search_view.remove_prefix(1);
10337
  }
10338
10339
  auto hash_view = url->get_hash();
10340
  if (hash_view.starts_with("#")) {
10341
    hash_view.remove_prefix(1);
10342
  }
10343
10344
  return test_components(protocol_view, url->get_username(),
10345
                         url->get_password(), url->get_hostname(),
10346
                         url->get_port(), url->get_pathname(), search_view,
10347
                         hash_view);
10348
}
10349
10350
template <url_pattern_regex::regex_concept regex_provider>
10351
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::match(
10352
    const url_pattern_input& input, const std::string_view* base_url_string) {
10353
  std::string protocol{};
10354
  std::string username{};
10355
  std::string password{};
10356
  std::string hostname{};
10357
  std::string port{};
10358
  std::string pathname{};
10359
  std::string search{};
10360
  std::string hash{};
10361
10362
  // Let inputs be an empty list.
10363
  // Append input to inputs.
10364
  std::vector inputs{input};
10365
10366
  // If input is a URLPatternInit then:
10367
  if (std::holds_alternative<url_pattern_init>(input)) {
10368
    ada_log(
10369
        "url_pattern::match called with url_pattern_init and base_url_string=",
10370
        base_url_string);
10371
    // If baseURLString was given, throw a TypeError.
10372
    if (base_url_string) {
10373
      ada_log("failed to match because base_url_string was given");
10374
      return tl::unexpected(errors::type_error);
10375
    }
10376
10377
    // Let applyResult be the result of process a URLPatternInit given input,
10378
    // "url", protocol, username, password, hostname, port, pathname, search,
10379
    // and hash.
10380
    auto apply_result = url_pattern_init::process(
10381
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10382
        protocol, username, password, hostname, port, pathname, search, hash);
10383
10384
    // If this throws an exception, catch it, and return null.
10385
    if (!apply_result.has_value()) {
10386
      ada_log("match returned std::nullopt because process threw");
10387
      return std::nullopt;
10388
    }
10389
10390
    // Set protocol to applyResult["protocol"].
10391
    ADA_ASSERT_TRUE(apply_result->protocol.has_value());
10392
    protocol = std::move(apply_result->protocol.value());
10393
10394
    // Set username to applyResult["username"].
10395
    ADA_ASSERT_TRUE(apply_result->username.has_value());
10396
    username = std::move(apply_result->username.value());
10397
10398
    // Set password to applyResult["password"].
10399
    ADA_ASSERT_TRUE(apply_result->password.has_value());
10400
    password = std::move(apply_result->password.value());
10401
10402
    // Set hostname to applyResult["hostname"].
10403
    ADA_ASSERT_TRUE(apply_result->hostname.has_value());
10404
    hostname = std::move(apply_result->hostname.value());
10405
10406
    // Set port to applyResult["port"].
10407
    ADA_ASSERT_TRUE(apply_result->port.has_value());
10408
    port = std::move(apply_result->port.value());
10409
10410
    // Set pathname to applyResult["pathname"].
10411
    ADA_ASSERT_TRUE(apply_result->pathname.has_value());
10412
    pathname = std::move(apply_result->pathname.value());
10413
10414
    // Set search to applyResult["search"].
10415
    ADA_ASSERT_TRUE(apply_result->search.has_value());
10416
    if (apply_result->search->starts_with("?")) {
10417
      search = apply_result->search->substr(1);
10418
    } else {
10419
      search = std::move(apply_result->search.value());
10420
    }
10421
10422
    // Set hash to applyResult["hash"].
10423
    ADA_ASSERT_TRUE(apply_result->hash.has_value());
10424
    ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#"));
10425
    hash = std::move(apply_result->hash.value());
10426
  } else {
10427
    ADA_ASSERT_TRUE(std::holds_alternative<std::string_view>(input));
10428
10429
    // Let baseURL be null.
10430
    result<url_aggregator> base_url;
10431
10432
    // If baseURLString was given, then:
10433
    if (base_url_string) {
10434
      // Let baseURL be the result of parsing baseURLString.
10435
      base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10436
10437
      // If baseURL is failure, return null.
10438
      if (!base_url) {
10439
        ada_log("match returned std::nullopt because failed to parse base_url=",
10440
                *base_url_string);
10441
        return std::nullopt;
10442
      }
10443
10444
      // Append baseURLString to inputs.
10445
      inputs.emplace_back(*base_url_string);
10446
    }
10447
10448
    url_aggregator* base_url_value =
10449
        base_url.has_value() ? &*base_url : nullptr;
10450
10451
    // Set url to the result of parsing input given baseURL.
10452
    auto url = ada::parse<url_aggregator>(std::get<std::string_view>(input),
10453
                                          base_url_value);
10454
10455
    // If url is failure, return null.
10456
    if (!url) {
10457
      ada_log("match returned std::nullopt because url failed");
10458
      return std::nullopt;
10459
    }
10460
10461
    // Set protocol to url's scheme.
10462
    // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':'
10463
    // is removed. Similar work was done on workerd:
10464
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038
10465
    protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1);
10466
    // Set username to url's username.
10467
    username = url->get_username();
10468
    // Set password to url's password.
10469
    password = url->get_password();
10470
    // Set hostname to url's host, serialized, or the empty string if the value
10471
    // is null.
10472
    hostname = url->get_hostname();
10473
    // Set port to url's port, serialized, or the empty string if the value is
10474
    // null.
10475
    port = url->get_port();
10476
    // Set pathname to the result of URL path serializing url.
10477
    pathname = url->get_pathname();
10478
    // Set search to url's query or the empty string if the value is null.
10479
    // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?'
10480
    // is removed. Similar work was done on workerd:
10481
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232
10482
    if (url->has_search()) {
10483
      auto view = url->get_search();
10484
      search = view.starts_with("?") ? url->get_search().substr(1) : view;
10485
    }
10486
    // Set hash to url's fragment or the empty string if the value is null.
10487
    // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is
10488
    // removed. Similar work was done on workerd:
10489
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242
10490
    if (url->has_hash()) {
10491
      auto view = url->get_hash();
10492
      hash = view.starts_with("#") ? url->get_hash().substr(1) : view;
10493
    }
10494
  }
10495
10496
  // Use fast_match which skips regex for simple patterns (EMPTY, EXACT_MATCH,
10497
  // FULL_WILDCARD) and only falls back to regex for complex REGEXP patterns.
10498
10499
  // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol
10500
  // component's regular expression, protocol).
10501
  auto protocol_exec_result = protocol_component.fast_match(protocol);
10502
  if (!protocol_exec_result) {
10503
    return std::nullopt;
10504
  }
10505
10506
  // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username
10507
  // component's regular expression, username).
10508
  auto username_exec_result = username_component.fast_match(username);
10509
  if (!username_exec_result) {
10510
    return std::nullopt;
10511
  }
10512
10513
  // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password
10514
  // component's regular expression, password).
10515
  auto password_exec_result = password_component.fast_match(password);
10516
  if (!password_exec_result) {
10517
    return std::nullopt;
10518
  }
10519
10520
  // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname
10521
  // component's regular expression, hostname).
10522
  auto hostname_exec_result = hostname_component.fast_match(hostname);
10523
  if (!hostname_exec_result) {
10524
    return std::nullopt;
10525
  }
10526
10527
  // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's
10528
  // regular expression, port).
10529
  auto port_exec_result = port_component.fast_match(port);
10530
  if (!port_exec_result) {
10531
    return std::nullopt;
10532
  }
10533
10534
  // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname
10535
  // component's regular expression, pathname).
10536
  auto pathname_exec_result = pathname_component.fast_match(pathname);
10537
  if (!pathname_exec_result) {
10538
    return std::nullopt;
10539
  }
10540
10541
  // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's
10542
  // regular expression, search).
10543
  auto search_exec_result = search_component.fast_match(search);
10544
  if (!search_exec_result) {
10545
    return std::nullopt;
10546
  }
10547
10548
  // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's
10549
  // regular expression, hash).
10550
  auto hash_exec_result = hash_component.fast_match(hash);
10551
  if (!hash_exec_result) {
10552
    return std::nullopt;
10553
  }
10554
10555
  // Let result be a new URLPatternResult.
10556
  auto result = url_pattern_result{};
10557
  // Set result["inputs"] to inputs.
10558
  result.inputs = std::move(inputs);
10559
  // Set result["protocol"] to the result of creating a component match result
10560
  // given urlPattern's protocol component, protocol, and protocolExecResult.
10561
  result.protocol = protocol_component.create_component_match_result(
10562
      std::move(protocol), std::move(*protocol_exec_result));
10563
10564
  // Set result["username"] to the result of creating a component match result
10565
  // given urlPattern's username component, username, and usernameExecResult.
10566
  result.username = username_component.create_component_match_result(
10567
      std::move(username), std::move(*username_exec_result));
10568
10569
  // Set result["password"] to the result of creating a component match result
10570
  // given urlPattern's password component, password, and passwordExecResult.
10571
  result.password = password_component.create_component_match_result(
10572
      std::move(password), std::move(*password_exec_result));
10573
10574
  // Set result["hostname"] to the result of creating a component match result
10575
  // given urlPattern's hostname component, hostname, and hostnameExecResult.
10576
  result.hostname = hostname_component.create_component_match_result(
10577
      std::move(hostname), std::move(*hostname_exec_result));
10578
10579
  // Set result["port"] to the result of creating a component match result given
10580
  // urlPattern's port component, port, and portExecResult.
10581
  result.port = port_component.create_component_match_result(
10582
      std::move(port), std::move(*port_exec_result));
10583
10584
  // Set result["pathname"] to the result of creating a component match result
10585
  // given urlPattern's pathname component, pathname, and pathnameExecResult.
10586
  result.pathname = pathname_component.create_component_match_result(
10587
      std::move(pathname), std::move(*pathname_exec_result));
10588
10589
  // Set result["search"] to the result of creating a component match result
10590
  // given urlPattern's search component, search, and searchExecResult.
10591
  result.search = search_component.create_component_match_result(
10592
      std::move(search), std::move(*search_exec_result));
10593
10594
  // Set result["hash"] to the result of creating a component match result given
10595
  // urlPattern's hash component, hash, and hashExecResult.
10596
  result.hash = hash_component.create_component_match_result(
10597
      std::move(hash), std::move(*hash_exec_result));
10598
10599
  return result;
10600
}
10601
10602
}  // namespace ada
10603
#endif  // ADA_INCLUDE_URL_PATTERN
10604
#endif
10605
/* end file include/ada/url_pattern-inl.h */
10606
/* begin file include/ada/url_pattern_helpers-inl.h */
10607
/**
10608
 * @file url_pattern_helpers-inl.h
10609
 * @brief Declaration for the URLPattern helpers.
10610
 */
10611
#ifndef ADA_URL_PATTERN_HELPERS_INL_H
10612
#define ADA_URL_PATTERN_HELPERS_INL_H
10613
10614
#include <optional>
10615
#include <string_view>
10616
10617
10618
#if ADA_INCLUDE_URL_PATTERN
10619
namespace ada::url_pattern_helpers {
10620
#if defined(ADA_TESTING) || defined(ADA_LOGGING)
10621
0
inline std::string to_string(token_type type) {
10622
0
  switch (type) {
10623
0
    case token_type::INVALID_CHAR:
10624
0
      return "INVALID_CHAR";
10625
0
    case token_type::OPEN:
10626
0
      return "OPEN";
10627
0
    case token_type::CLOSE:
10628
0
      return "CLOSE";
10629
0
    case token_type::REGEXP:
10630
0
      return "REGEXP";
10631
0
    case token_type::NAME:
10632
0
      return "NAME";
10633
0
    case token_type::CHAR:
10634
0
      return "CHAR";
10635
0
    case token_type::ESCAPED_CHAR:
10636
0
      return "ESCAPED_CHAR";
10637
0
    case token_type::OTHER_MODIFIER:
10638
0
      return "OTHER_MODIFIER";
10639
0
    case token_type::ASTERISK:
10640
0
      return "ASTERISK";
10641
0
    case token_type::END:
10642
0
      return "END";
10643
0
    default:
10644
0
      ada::unreachable();
10645
0
  }
10646
0
}
10647
#endif  // defined(ADA_TESTING) || defined(ADA_LOGGING)
10648
10649
template <url_pattern_regex::regex_concept regex_provider>
10650
constexpr void constructor_string_parser<regex_provider>::rewind() {
10651
  // Set parser's token index to parser's component start.
10652
  token_index = component_start;
10653
  // Set parser's token increment to 0.
10654
  token_increment = 0;
10655
}
10656
10657
template <url_pattern_regex::regex_concept regex_provider>
10658
constexpr bool constructor_string_parser<regex_provider>::is_hash_prefix() {
10659
  // Return the result of running is a non-special pattern char given parser,
10660
  // parser's token index and "#".
10661
  return is_non_special_pattern_char(token_index, '#');
10662
}
10663
10664
template <url_pattern_regex::regex_concept regex_provider>
10665
constexpr bool constructor_string_parser<regex_provider>::is_search_prefix() {
10666
  // If result of running is a non-special pattern char given parser, parser's
10667
  // token index and "?" is true, then return true.
10668
  if (is_non_special_pattern_char(token_index, '?')) {
10669
    return true;
10670
  }
10671
10672
  // If parser's token list[parser's token index]'s value is not "?", then
10673
  // return false.
10674
  if (token_list[token_index].value != "?") {
10675
    return false;
10676
  }
10677
10678
  // If previous index is less than 0, then return true.
10679
  if (token_index == 0) return true;
10680
  // Let previous index be parser's token index - 1.
10681
  auto previous_index = token_index - 1;
10682
  // Let previous token be the result of running get a safe token given parser
10683
  // and previous index.
10684
  auto previous_token = get_safe_token(previous_index);
10685
  ADA_ASSERT_TRUE(previous_token);
10686
  // If any of the following are true, then return false:
10687
  // - previous token's type is "name".
10688
  // - previous token's type is "regexp".
10689
  // - previous token's type is "close".
10690
  // - previous token's type is "asterisk".
10691
  return !(previous_token->type == token_type::NAME ||
10692
           previous_token->type == token_type::REGEXP ||
10693
           previous_token->type == token_type::CLOSE ||
10694
           previous_token->type == token_type::ASTERISK);
10695
}
10696
10697
template <url_pattern_regex::regex_concept regex_provider>
10698
constexpr bool
10699
constructor_string_parser<regex_provider>::is_non_special_pattern_char(
10700
    size_t index, uint32_t value) const {
10701
  // Let token be the result of running get a safe token given parser and index.
10702
  auto token = get_safe_token(index);
10703
  ADA_ASSERT_TRUE(token);
10704
10705
  // If token's value is not value, then return false.
10706
  // TODO: Remove this once we make sure get_safe_token returns a non-empty
10707
  // string.
10708
  if (!token->value.empty() &&
10709
      static_cast<uint32_t>(token->value[0]) != value) {
10710
    return false;
10711
  }
10712
10713
  // If any of the following are true:
10714
  // - token's type is "char";
10715
  // - token's type is "escaped-char"; or
10716
  // - token's type is "invalid-char",
10717
  // - then return true.
10718
  return token->type == token_type::CHAR ||
10719
         token->type == token_type::ESCAPED_CHAR ||
10720
         token->type == token_type::INVALID_CHAR;
10721
}
10722
10723
template <url_pattern_regex::regex_concept regex_provider>
10724
constexpr const token*
10725
constructor_string_parser<regex_provider>::get_safe_token(size_t index) const {
10726
  // If index is less than parser's token list's size, then return parser's
10727
  // token list[index].
10728
  if (index < token_list.size()) [[likely]] {
10729
    return &token_list[index];
10730
  }
10731
10732
  // Assert: parser's token list's size is greater than or equal to 1.
10733
  ADA_ASSERT_TRUE(!token_list.empty());
10734
10735
  // Let token be parser's token list[last index].
10736
  // Assert: token's type is "end".
10737
  ADA_ASSERT_TRUE(token_list.back().type == token_type::END);
10738
10739
  // Return token.
10740
  return &token_list.back();
10741
}
10742
10743
template <url_pattern_regex::regex_concept regex_provider>
10744
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10745
    const {
10746
  // If parser's token list[parser's token index]'s type is "open", then return
10747
  // true.
10748
  return token_list[token_index].type == token_type::OPEN;
10749
}
10750
10751
template <url_pattern_regex::regex_concept regex_provider>
10752
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10753
    const {
10754
  // If parser's token list[parser's token index]'s type is "close", then return
10755
  // true.
10756
  return token_list[token_index].type == token_type::CLOSE;
10757
}
10758
10759
template <url_pattern_regex::regex_concept regex_provider>
10760
constexpr bool
10761
constructor_string_parser<regex_provider>::next_is_authority_slashes() const {
10762
  // If the result of running is a non-special pattern char given parser,
10763
  // parser's token index + 1, and "/" is false, then return false.
10764
  if (!is_non_special_pattern_char(token_index + 1, '/')) {
10765
    return false;
10766
  }
10767
  // If the result of running is a non-special pattern char given parser,
10768
  // parser's token index + 2, and "/" is false, then return false.
10769
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10770
    return false;
10771
  }
10772
  return true;
10773
}
10774
10775
template <url_pattern_regex::regex_concept regex_provider>
10776
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10777
    const {
10778
  // Return the result of running is a non-special pattern char given parser,
10779
  // parser's token index, and ":".
10780
  return is_non_special_pattern_char(token_index, ':');
10781
}
10782
10783
template <url_pattern_regex::regex_concept regex_provider>
10784
void constructor_string_parser<regex_provider>::change_state(State new_state,
10785
                                                             size_t skip) {
10786
  // If parser's state is not "init", not "authority", and not "done", then set
10787
  // parser's result[parser's state] to the result of running make a component
10788
  // string given parser.
10789
  if (state != State::INIT && state != State::AUTHORITY &&
10790
      state != State::DONE) {
10791
    auto value = make_component_string();
10792
    // TODO: Simplify this.
10793
    switch (state) {
10794
      case State::PROTOCOL: {
10795
        result.protocol = value;
10796
        break;
10797
      }
10798
      case State::USERNAME: {
10799
        result.username = value;
10800
        break;
10801
      }
10802
      case State::PASSWORD: {
10803
        result.password = value;
10804
        break;
10805
      }
10806
      case State::HOSTNAME: {
10807
        result.hostname = value;
10808
        break;
10809
      }
10810
      case State::PORT: {
10811
        result.port = value;
10812
        break;
10813
      }
10814
      case State::PATHNAME: {
10815
        result.pathname = value;
10816
        break;
10817
      }
10818
      case State::SEARCH: {
10819
        result.search = value;
10820
        break;
10821
      }
10822
      case State::HASH: {
10823
        result.hash = value;
10824
        break;
10825
      }
10826
      default:
10827
        ada::unreachable();
10828
    }
10829
  }
10830
10831
  // If parser's state is not "init" and new state is not "done", then:
10832
  if (state != State::INIT && new_state != State::DONE) {
10833
    // If parser's state is "protocol", "authority", "username", or "password";
10834
    // new state is "port", "pathname", "search", or "hash"; and parser's
10835
    // result["hostname"] does not exist, then set parser's result["hostname"]
10836
    // to the empty string.
10837
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10838
         state == State::USERNAME || state == State::PASSWORD) &&
10839
        (new_state == State::PORT || new_state == State::PATHNAME ||
10840
         new_state == State::SEARCH || new_state == State::HASH) &&
10841
        !result.hostname)
10842
      result.hostname = "";
10843
  }
10844
10845
  // If parser's state is "protocol", "authority", "username", "password",
10846
  // "hostname", or "port"; new state is "search" or "hash"; and parser's
10847
  // result["pathname"] does not exist, then:
10848
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10849
       state == State::USERNAME || state == State::PASSWORD ||
10850
       state == State::HOSTNAME || state == State::PORT) &&
10851
      (new_state == State::SEARCH || new_state == State::HASH) &&
10852
      !result.pathname) {
10853
    if (protocol_matches_a_special_scheme_flag) {
10854
      result.pathname = "/";
10855
    } else {
10856
      // Otherwise, set parser's result["pathname"] to the empty string.
10857
      result.pathname = "";
10858
    }
10859
  }
10860
10861
  // If parser's state is "protocol", "authority", "username", "password",
10862
  // "hostname", "port", or "pathname"; new state is "hash"; and parser's
10863
  // result["search"] does not exist, then set parser's result["search"] to
10864
  // the empty string.
10865
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10866
       state == State::USERNAME || state == State::PASSWORD ||
10867
       state == State::HOSTNAME || state == State::PORT ||
10868
       state == State::PATHNAME) &&
10869
      new_state == State::HASH && !result.search) {
10870
    result.search = "";
10871
  }
10872
10873
  // Set parser's state to new state.
10874
  state = new_state;
10875
  // Increment parser's token index by skip.
10876
  token_index += skip;
10877
  // Set parser's component start to parser's token index.
10878
  component_start = token_index;
10879
  // Set parser's token increment to 0.
10880
  token_increment = 0;
10881
}
10882
10883
template <url_pattern_regex::regex_concept regex_provider>
10884
std::string constructor_string_parser<regex_provider>::make_component_string() {
10885
  // Assert: parser's token index is less than parser's token list's size.
10886
  ADA_ASSERT_TRUE(token_index < token_list.size());
10887
10888
  // Let token be parser's token list[parser's token index].
10889
  // Let end index be token's index.
10890
  const auto end_index = token_list[token_index].index;
10891
  // Let component start token be the result of running get a safe token given
10892
  // parser and parser's component start.
10893
  const auto component_start_token = get_safe_token(component_start);
10894
  ADA_ASSERT_TRUE(component_start_token);
10895
  // Let component start input index be component start token's index.
10896
  const auto component_start_input_index = component_start_token->index;
10897
  // Return the code point substring from component start input index to end
10898
  // index within parser's input.
10899
  return std::string(input.substr(component_start_input_index,
10900
                                  end_index - component_start_input_index));
10901
}
10902
10903
template <url_pattern_regex::regex_concept regex_provider>
10904
constexpr bool
10905
constructor_string_parser<regex_provider>::is_an_identity_terminator() const {
10906
  // Return the result of running is a non-special pattern char given parser,
10907
  // parser's token index, and "@".
10908
  return is_non_special_pattern_char(token_index, '@');
10909
}
10910
10911
template <url_pattern_regex::regex_concept regex_provider>
10912
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
10913
    const {
10914
  // Return the result of running is a non-special pattern char given parser,
10915
  // parser's token index, and "/".
10916
  return is_non_special_pattern_char(token_index, '/');
10917
}
10918
10919
template <url_pattern_regex::regex_concept regex_provider>
10920
constexpr bool constructor_string_parser<regex_provider>::is_password_prefix()
10921
    const {
10922
  // Return the result of running is a non-special pattern char given parser,
10923
  // parser's token index, and ":".
10924
  return is_non_special_pattern_char(token_index, ':');
10925
}
10926
10927
template <url_pattern_regex::regex_concept regex_provider>
10928
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_open()
10929
    const {
10930
  // Return the result of running is a non-special pattern char given parser,
10931
  // parser's token index, and "[".
10932
  return is_non_special_pattern_char(token_index, '[');
10933
}
10934
10935
template <url_pattern_regex::regex_concept regex_provider>
10936
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
10937
    const {
10938
  // Return the result of running is a non-special pattern char given parser,
10939
  // parser's token index, and "]".
10940
  return is_non_special_pattern_char(token_index, ']');
10941
}
10942
10943
template <url_pattern_regex::regex_concept regex_provider>
10944
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
10945
    const {
10946
  // Return the result of running is a non-special pattern char given parser,
10947
  // parser's token index, and ":".
10948
  return is_non_special_pattern_char(token_index, ':');
10949
}
10950
10951
0
constexpr void Tokenizer::get_next_code_point() {
10952
0
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
10953
0
  ADA_ASSERT_TRUE(next_index < input.size());
10954
  // Decode the next UTF-8 code point. If malformed or truncated, mark it as
10955
  // invalid, return the offending byte as the code point, and advance by one
10956
  // to guarantee forward progress.
10957
0
  invalid_code_point = false;
10958
0
  code_point = 0;
10959
0
  size_t number_bytes = 0;
10960
0
  const size_t initial_index = next_index;
10961
0
  unsigned char first_byte = input[next_index];
10962
10963
0
  if ((first_byte & 0x80) == 0) {
10964
    // 1-byte character (ASCII)
10965
0
    next_index++;
10966
0
    code_point = first_byte;
10967
0
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
10968
0
            uint32_t(code_point));
10969
0
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10970
0
            " input.size()=", input.size());
10971
0
    return;
10972
0
  }
10973
0
  ada_log("Tokenizer::get_next_code_point read first byte=",
10974
0
          uint32_t(first_byte));
10975
0
  if ((first_byte & 0xE0) == 0xC0) {
10976
0
    code_point = first_byte & 0x1F;
10977
0
    number_bytes = 2;
10978
0
    ada_log("Tokenizer::get_next_code_point two bytes");
10979
0
  } else if ((first_byte & 0xF0) == 0xE0) {
10980
0
    code_point = first_byte & 0x0F;
10981
0
    number_bytes = 3;
10982
0
    ada_log("Tokenizer::get_next_code_point three bytes");
10983
0
  } else if ((first_byte & 0xF8) == 0xF0) {
10984
0
    code_point = first_byte & 0x07;
10985
0
    number_bytes = 4;
10986
0
    ada_log("Tokenizer::get_next_code_point four bytes");
10987
0
  }
10988
10989
  // Invalid leading bytes that still match a multi-byte prefix.
10990
0
  if ((number_bytes == 2 && first_byte < 0xC2) ||
10991
0
      (number_bytes == 4 && first_byte > 0xF4)) {
10992
0
    invalid_code_point = true;
10993
0
    code_point = first_byte;
10994
0
    next_index = initial_index + 1;
10995
0
    return;
10996
0
  }
10997
10998
  // Invalid leading byte (e.g., continuation byte outside a sequence).
10999
0
  if (number_bytes == 0) {
11000
0
    invalid_code_point = true;
11001
0
    code_point = first_byte;
11002
0
    next_index = initial_index + 1;
11003
0
    return;
11004
0
  }
11005
11006
  // Truncated UTF-8 sequence.
11007
0
  if (number_bytes + next_index > input.size()) {
11008
0
    invalid_code_point = true;
11009
0
    code_point = first_byte;
11010
0
    next_index = initial_index + 1;
11011
0
    return;
11012
0
  }
11013
11014
0
  for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) {
11015
0
    unsigned char byte = input[i];
11016
0
    if ((byte & 0xC0) != 0x80) {
11017
0
      invalid_code_point = true;
11018
0
      code_point = first_byte;
11019
0
      next_index = initial_index + 1;
11020
0
      return;
11021
0
    }
11022
0
    ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte));
11023
0
    code_point = (code_point << 6) | (byte & 0x3F);
11024
0
  }
11025
0
  ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=",
11026
0
          uint32_t(code_point));
11027
0
  ada_log("Tokenizer::get_next_code_point next_index =", next_index,
11028
0
          " input.size()=", input.size());
11029
0
  next_index += number_bytes;
11030
0
}
11031
11032
0
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
11033
0
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
11034
0
          new_index);
11035
  // Set tokenizer's next index to index.
11036
0
  next_index = new_index;
11037
  // Run get the next code point given tokenizer.
11038
0
  get_next_code_point();
11039
0
}
11040
11041
inline void Tokenizer::add_token(token_type type, size_t next_position,
11042
0
                                 size_t value_position, size_t value_length) {
11043
0
  ada_log("Tokenizer::add_token called with type=", to_string(type),
11044
0
          " next_position=", next_position, " value_position=", value_position);
11045
0
  ADA_ASSERT_TRUE(next_position >= value_position);
11046
11047
  // Let token be a new token.
11048
  // Set token's type to type.
11049
  // Set token's index to tokenizer's index.
11050
  // Set token's value to the code point substring from value position with
11051
  // length value length within tokenizer's input.
11052
  // Append token to the back of tokenizer's token list.
11053
0
  token_list.emplace_back(type, index,
11054
0
                          input.substr(value_position, value_length));
11055
  // Set tokenizer's index to next position.
11056
0
  index = next_position;
11057
0
}
11058
11059
inline void Tokenizer::add_token_with_default_length(token_type type,
11060
                                                     size_t next_position,
11061
0
                                                     size_t value_position) {
11062
  // Let computed length be next position - value position.
11063
0
  auto computed_length = next_position - value_position;
11064
  // Run add a token given tokenizer, type, next position, value position, and
11065
  // computed length.
11066
0
  add_token(type, next_position, value_position, computed_length);
11067
0
}
11068
11069
0
inline void Tokenizer::add_token_with_defaults(token_type type) {
11070
0
  ada_log("Tokenizer::add_token_with_defaults called with type=",
11071
0
          to_string(type));
11072
  // Run add a token with default length given tokenizer, type, tokenizer's next
11073
  // index, and tokenizer's index.
11074
0
  add_token_with_default_length(type, next_index, index);
11075
0
}
11076
11077
inline ada_warn_unused std::optional<errors>
11078
Tokenizer::process_tokenizing_error(size_t next_position,
11079
0
                                    size_t value_position) {
11080
  // If tokenizer's policy is "strict", then throw a TypeError.
11081
0
  if (policy == token_policy::strict) {
11082
0
    ada_log("process_tokenizing_error failed with next_position=",
11083
0
            next_position, " value_position=", value_position);
11084
0
    return errors::type_error;
11085
0
  }
11086
  // Assert: tokenizer's policy is "lenient".
11087
0
  ADA_ASSERT_TRUE(policy == token_policy::lenient);
11088
  // Run add a token with default length given tokenizer, "invalid-char", next
11089
  // position, and value position.
11090
0
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
11091
0
                                value_position);
11092
0
  return std::nullopt;
11093
0
}
11094
11095
template <url_pattern_encoding_callback F>
11096
0
token* url_pattern_parser<F>::try_consume_modifier_token() {
11097
  // Let token be the result of running try to consume a token given parser and
11098
  // "other-modifier".
11099
0
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
11100
  // If token is not null, then return token.
11101
0
  if (token) return token;
11102
  // Set token to the result of running try to consume a token given parser and
11103
  // "asterisk".
11104
  // Return token.
11105
0
  return try_consume_token(token_type::ASTERISK);
11106
0
}
11107
11108
template <url_pattern_encoding_callback F>
11109
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
11110
0
    const token* name_token) {
11111
  // Let token be the result of running try to consume a token given parser and
11112
  // "regexp".
11113
0
  auto token = try_consume_token(token_type::REGEXP);
11114
  // If name token is null and token is null, then set token to the result of
11115
  // running try to consume a token given parser and "asterisk".
11116
0
  if (!name_token && !token) {
11117
0
    token = try_consume_token(token_type::ASTERISK);
11118
0
  }
11119
  // Return token.
11120
0
  return token;
11121
0
}
11122
11123
template <url_pattern_encoding_callback F>
11124
0
token* url_pattern_parser<F>::try_consume_token(token_type type) {
11125
0
  ada_log("url_pattern_parser::try_consume_token called with type=",
11126
0
          to_string(type));
11127
  // Assert: parser's index is less than parser's token list size.
11128
0
  ADA_ASSERT_TRUE(index < tokens.size());
11129
  // Let next token be parser's token list[parser's index].
11130
0
  auto& next_token = tokens[index];
11131
  // If next token's type is not type return null.
11132
0
  if (next_token.type != type) return nullptr;
11133
  // Increase parser's index by 1.
11134
0
  index++;
11135
  // Return next token.
11136
0
  return &next_token;
11137
0
}
11138
11139
template <url_pattern_encoding_callback F>
11140
0
std::string url_pattern_parser<F>::consume_text() {
11141
  // Let result be the empty string.
11142
0
  std::string result{};
11143
  // While true:
11144
0
  while (true) {
11145
    // Let token be the result of running try to consume a token given parser
11146
    // and "char".
11147
0
    auto token = try_consume_token(token_type::CHAR);
11148
    // If token is null, then set token to the result of running try to consume
11149
    // a token given parser and "escaped-char".
11150
0
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
11151
    // If token is null, then break.
11152
0
    if (!token) break;
11153
    // Append token's value to the end of result.
11154
0
    result.append(token->value);
11155
0
  }
11156
  // Return result.
11157
0
  return result;
11158
0
}
11159
11160
template <url_pattern_encoding_callback F>
11161
0
bool url_pattern_parser<F>::consume_required_token(token_type type) {
11162
0
  ada_log("url_pattern_parser::consume_required_token called with type=",
11163
0
          to_string(type));
11164
  // Let result be the result of running try to consume a token given parser and
11165
  // type.
11166
0
  return try_consume_token(type) != nullptr;
11167
0
}
11168
11169
template <url_pattern_encoding_callback F>
11170
std::optional<errors>
11171
0
url_pattern_parser<F>::maybe_add_part_from_the_pending_fixed_value() {
11172
  // If parser's pending fixed value is the empty string, then return.
11173
0
  if (pending_fixed_value.empty()) {
11174
0
    ada_log("pending_fixed_value is empty");
11175
0
    return std::nullopt;
11176
0
  }
11177
  // Let encoded value be the result of running parser's encoding callback given
11178
  // parser's pending fixed value.
11179
0
  auto encoded_value = encoding_callback(pending_fixed_value);
11180
0
  if (!encoded_value) {
11181
0
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
11182
0
    return encoded_value.error();
11183
0
  }
11184
  // Set parser's pending fixed value to the empty string.
11185
0
  pending_fixed_value.clear();
11186
  // Let part be a new part whose type is "fixed-text", value is encoded value,
11187
  // and modifier is "none".
11188
  // Append part to parser's part list.
11189
0
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11190
0
                     std::move(*encoded_value),
11191
0
                     url_pattern_part_modifier::none);
11192
0
  return std::nullopt;
11193
0
}
11194
11195
template <url_pattern_encoding_callback F>
11196
std::optional<errors> url_pattern_parser<F>::add_part(
11197
    std::string_view prefix, token* name_token, token* regexp_or_wildcard_token,
11198
0
    std::string_view suffix, token* modifier_token) {
11199
  // Let modifier be "none".
11200
0
  auto modifier = url_pattern_part_modifier::none;
11201
  // If modifier token is not null:
11202
0
  if (modifier_token) {
11203
    // If modifier token's value is "?" then set modifier to "optional".
11204
0
    if (modifier_token->value == "?") {
11205
0
      modifier = url_pattern_part_modifier::optional;
11206
0
    } else if (modifier_token->value == "*") {
11207
      // Otherwise if modifier token's value is "*" then set modifier to
11208
      // "zero-or-more".
11209
0
      modifier = url_pattern_part_modifier::zero_or_more;
11210
0
    } else if (modifier_token->value == "+") {
11211
      // Otherwise if modifier token's value is "+" then set modifier to
11212
      // "one-or-more".
11213
0
      modifier = url_pattern_part_modifier::one_or_more;
11214
0
    }
11215
0
  }
11216
  // If name token is null and regexp or wildcard token is null and modifier
11217
  // is "none":
11218
0
  if (!name_token && !regexp_or_wildcard_token &&
11219
0
      modifier == url_pattern_part_modifier::none) {
11220
    // Append prefix to the end of parser's pending fixed value.
11221
0
    pending_fixed_value.append(prefix);
11222
0
    return std::nullopt;
11223
0
  }
11224
  // Run maybe add a part from the pending fixed value given parser.
11225
0
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
11226
0
    return *error;
11227
0
  }
11228
  // If name token is null and regexp or wildcard token is null:
11229
0
  if (!name_token && !regexp_or_wildcard_token) {
11230
    // Assert: suffix is the empty string.
11231
0
    ADA_ASSERT_TRUE(suffix.empty());
11232
    // If prefix is the empty string, then return.
11233
0
    if (prefix.empty()) return std::nullopt;
11234
    // Let encoded value be the result of running parser's encoding callback
11235
    // given prefix.
11236
0
    auto encoded_value = encoding_callback(prefix);
11237
0
    if (!encoded_value) {
11238
0
      return encoded_value.error();
11239
0
    }
11240
    // Let part be a new part whose type is "fixed-text", value is encoded
11241
    // value, and modifier is modifier.
11242
    // Append part to parser's part list.
11243
0
    parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11244
0
                       std::move(*encoded_value), modifier);
11245
0
    return std::nullopt;
11246
0
  }
11247
  // Let regexp value be the empty string.
11248
0
  std::string regexp_value{};
11249
  // If regexp or wildcard token is null, then set regexp value to parser's
11250
  // segment wildcard regexp.
11251
0
  if (!regexp_or_wildcard_token) {
11252
0
    regexp_value = segment_wildcard_regexp;
11253
0
  } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) {
11254
    // Otherwise if regexp or wildcard token's type is "asterisk", then set
11255
    // regexp value to the full wildcard regexp value.
11256
0
    regexp_value = ".*";
11257
0
  } else {
11258
    // Otherwise set regexp value to regexp or wildcard token's value.
11259
0
    regexp_value = regexp_or_wildcard_token->value;
11260
0
  }
11261
  // Let type be "regexp".
11262
0
  auto type = url_pattern_part_type::REGEXP;
11263
  // If regexp value is parser's segment wildcard regexp:
11264
0
  if (regexp_value == segment_wildcard_regexp) {
11265
    // Set type to "segment-wildcard".
11266
0
    type = url_pattern_part_type::SEGMENT_WILDCARD;
11267
    // Set regexp value to the empty string.
11268
0
    regexp_value.clear();
11269
0
  } else if (regexp_value == ".*") {
11270
    // Otherwise if regexp value is the full wildcard regexp value:
11271
    // Set type to "full-wildcard".
11272
0
    type = url_pattern_part_type::FULL_WILDCARD;
11273
    // Set regexp value to the empty string.
11274
0
    regexp_value.clear();
11275
0
  }
11276
  // Let name be the empty string.
11277
0
  std::string name{};
11278
  // If name token is not null, then set name to name token's value.
11279
0
  if (name_token) {
11280
0
    name = name_token->value;
11281
0
  } else if (regexp_or_wildcard_token != nullptr) {
11282
    // Otherwise if regexp or wildcard token is not null:
11283
    // Set name to parser's next numeric name, serialized.
11284
0
    name = std::to_string(next_numeric_name);
11285
    // Increment parser's next numeric name by 1.
11286
0
    next_numeric_name++;
11287
0
  }
11288
  // If the result of running is a duplicate name given parser and name is
11289
  // true, then throw a TypeError.
11290
0
  if (std::ranges::any_of(
11291
0
          parts, [&name](const auto& part) { return part.name == name; })) {
11292
0
    return errors::type_error;
11293
0
  }
11294
  // Let encoded prefix be the result of running parser's encoding callback
11295
  // given prefix.
11296
0
  auto encoded_prefix = encoding_callback(prefix);
11297
0
  if (!encoded_prefix) return encoded_prefix.error();
11298
  // Let encoded suffix be the result of running parser's encoding callback
11299
  // given suffix.
11300
0
  auto encoded_suffix = encoding_callback(suffix);
11301
0
  if (!encoded_suffix) return encoded_suffix.error();
11302
  // Let part be a new part whose type is type, value is regexp value,
11303
  // modifier is modifier, name is name, prefix is encoded prefix, and suffix
11304
  // is encoded suffix.
11305
  // Append part to parser's part list.
11306
0
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
11307
0
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
11308
0
  return std::nullopt;
11309
0
}
11310
11311
template <url_pattern_encoding_callback F>
11312
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
11313
    std::string_view input, url_pattern_compile_component_options& options,
11314
0
    F& encoding_callback) {
11315
0
  ada_log("parse_pattern_string input=", input);
11316
  // Let parser be a new pattern parser whose encoding callback is encoding
11317
  // callback and segment wildcard regexp is the result of running generate a
11318
  // segment wildcard regexp given options.
11319
0
  auto parser = url_pattern_parser<F>(
11320
0
      encoding_callback, generate_segment_wildcard_regexp(options));
11321
  // Set parser's token list to the result of running tokenize given input and
11322
  // "strict".
11323
0
  auto tokenize_result = tokenize(input, token_policy::strict);
11324
0
  if (!tokenize_result) {
11325
0
    ada_log("parse_pattern_string tokenize failed");
11326
0
    return tl::unexpected(tokenize_result.error());
11327
0
  }
11328
0
  parser.tokens = std::move(*tokenize_result);
11329
11330
  // While parser's index is less than parser's token list's size:
11331
0
  while (parser.can_continue()) {
11332
    // Let char token be the result of running try to consume a token given
11333
    // parser and "char".
11334
0
    auto char_token = parser.try_consume_token(token_type::CHAR);
11335
    // Let name token be the result of running try to consume a token given
11336
    // parser and "name".
11337
0
    auto name_token = parser.try_consume_token(token_type::NAME);
11338
    // Let regexp or wildcard token be the result of running try to consume a
11339
    // regexp or wildcard token given parser and name token.
11340
0
    auto regexp_or_wildcard_token =
11341
0
        parser.try_consume_regexp_or_wildcard_token(name_token);
11342
    // If name token is not null or regexp or wildcard token is not null:
11343
0
    if (name_token || regexp_or_wildcard_token) {
11344
      // Let prefix be the empty string.
11345
0
      std::string prefix{};
11346
      // If char token is not null then set prefix to char token's value.
11347
0
      if (char_token) prefix = char_token->value;
11348
      // If prefix is not the empty string and not options's prefix code point:
11349
0
      if (!prefix.empty() && prefix != options.get_prefix()) {
11350
        // Append prefix to the end of parser's pending fixed value.
11351
0
        parser.pending_fixed_value.append(prefix);
11352
        // Set prefix to the empty string.
11353
0
        prefix.clear();
11354
0
      }
11355
      // Run maybe add a part from the pending fixed value given parser.
11356
0
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11357
0
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
11358
0
        return tl::unexpected(*error);
11359
0
      }
11360
      // Let modifier token be the result of running try to consume a modifier
11361
      // token given parser.
11362
0
      auto modifier_token = parser.try_consume_modifier_token();
11363
      // Run add a part given parser, prefix, name token, regexp or wildcard
11364
      // token, the empty string, and modifier token.
11365
0
      if (auto error =
11366
0
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
11367
0
                              modifier_token)) {
11368
0
        ada_log("parser.add_part failed");
11369
0
        return tl::unexpected(*error);
11370
0
      }
11371
      // Continue.
11372
0
      continue;
11373
0
    }
11374
11375
    // Let fixed token be char token.
11376
0
    auto fixed_token = char_token;
11377
    // If fixed token is null, then set fixed token to the result of running try
11378
    // to consume a token given parser and "escaped-char".
11379
0
    if (!fixed_token)
11380
0
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
11381
    // If fixed token is not null:
11382
0
    if (fixed_token) {
11383
      // Append fixed token's value to parser's pending fixed value.
11384
0
      parser.pending_fixed_value.append(fixed_token->value);
11385
      // Continue.
11386
0
      continue;
11387
0
    }
11388
    // Let open token be the result of running try to consume a token given
11389
    // parser and "open".
11390
0
    auto open_token = parser.try_consume_token(token_type::OPEN);
11391
    // If open token is not null:
11392
0
    if (open_token) {
11393
      // Set prefix be the result of running consume text given parser.
11394
0
      auto prefix_ = parser.consume_text();
11395
      // Set name token to the result of running try to consume a token given
11396
      // parser and "name".
11397
0
      name_token = parser.try_consume_token(token_type::NAME);
11398
      // Set regexp or wildcard token to the result of running try to consume a
11399
      // regexp or wildcard token given parser and name token.
11400
0
      regexp_or_wildcard_token =
11401
0
          parser.try_consume_regexp_or_wildcard_token(name_token);
11402
      // Let suffix be the result of running consume text given parser.
11403
0
      auto suffix_ = parser.consume_text();
11404
      // Run consume a required token given parser and "close".
11405
0
      if (!parser.consume_required_token(token_type::CLOSE)) {
11406
0
        ada_log("parser.consume_required_token failed");
11407
0
        return tl::unexpected(errors::type_error);
11408
0
      }
11409
      // Set modifier token to the result of running try to consume a modifier
11410
      // token given parser.
11411
0
      auto modifier_token = parser.try_consume_modifier_token();
11412
      // Run add a part given parser, prefix, name token, regexp or wildcard
11413
      // token, suffix, and modifier token.
11414
0
      if (auto error =
11415
0
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
11416
0
                              suffix_, modifier_token)) {
11417
0
        return tl::unexpected(*error);
11418
0
      }
11419
      // Continue.
11420
0
      continue;
11421
0
    }
11422
    // Run maybe add a part from the pending fixed value given parser.
11423
0
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11424
0
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
11425
0
      return tl::unexpected(*error);
11426
0
    }
11427
    // Run consume a required token given parser and "end".
11428
0
    if (!parser.consume_required_token(token_type::END)) {
11429
0
      return tl::unexpected(errors::type_error);
11430
0
    }
11431
0
  }
11432
0
  ada_log("parser.parts size is: ", parser.parts.size());
11433
  // Return parser's part list.
11434
0
  return parser.parts;
11435
0
}
11436
11437
template <url_pattern_regex::regex_concept regex_provider>
11438
bool protocol_component_matches_special_scheme(
11439
    url_pattern_component<regex_provider>& component) {
11440
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
11441
  switch (component.type) {
11442
    case url_pattern_component_type::EMPTY:
11443
      // Empty pattern can't match any special scheme
11444
      return false;
11445
    case url_pattern_component_type::EXACT_MATCH:
11446
      // Direct string comparison for exact match patterns
11447
      return component.exact_match_value == "http" ||
11448
             component.exact_match_value == "https" ||
11449
             component.exact_match_value == "ws" ||
11450
             component.exact_match_value == "wss" ||
11451
             component.exact_match_value == "ftp" ||
11452
             component.exact_match_value == "file";
11453
    case url_pattern_component_type::FULL_WILDCARD:
11454
      // Full wildcard matches everything including special schemes
11455
      return true;
11456
    case url_pattern_component_type::REGEXP:
11457
      // Fall back to regex matching for complex patterns
11458
      auto& regex = component.regexp;
11459
      return regex_provider::regex_match("http", regex) ||
11460
             regex_provider::regex_match("https", regex) ||
11461
             regex_provider::regex_match("ws", regex) ||
11462
             regex_provider::regex_match("wss", regex) ||
11463
             regex_provider::regex_match("ftp", regex) ||
11464
             regex_provider::regex_match("file", regex);
11465
  }
11466
  ada::unreachable();
11467
}
11468
11469
template <url_pattern_regex::regex_concept regex_provider>
11470
inline std::optional<errors> constructor_string_parser<
11471
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
11472
  ada_log(
11473
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
11474
      "flag");
11475
  // Let protocol string be the result of running make a component string given
11476
  // parser.
11477
  auto protocol_string = make_component_string();
11478
  // Let protocol component be the result of compiling a component given
11479
  // protocol string, canonicalize a protocol, and default options.
11480
  auto protocol_component = url_pattern_component<regex_provider>::compile(
11481
      protocol_string, canonicalize_protocol,
11482
      url_pattern_compile_component_options::DEFAULT);
11483
  if (!protocol_component) {
11484
    ada_log("url_pattern_component::compile failed for protocol_string ",
11485
            protocol_string);
11486
    return protocol_component.error();
11487
  }
11488
  // If the result of running protocol component matches a special scheme given
11489
  // protocol component is true, then set parser's protocol matches a special
11490
  // scheme flag to true.
11491
  if (protocol_component_matches_special_scheme(*protocol_component)) {
11492
    protocol_matches_a_special_scheme_flag = true;
11493
  }
11494
  return std::nullopt;
11495
}
11496
11497
template <url_pattern_regex::regex_concept regex_provider>
11498
tl::expected<url_pattern_init, errors>
11499
constructor_string_parser<regex_provider>::parse(std::string_view input) {
11500
  ada_log("constructor_string_parser::parse input=", input);
11501
  // Let parser be a new constructor string parser whose input is input and
11502
  // token list is the result of running tokenize given input and "lenient".
11503
  auto token_list = tokenize(input, token_policy::lenient);
11504
  if (!token_list) {
11505
    return tl::unexpected(token_list.error());
11506
  }
11507
  auto parser = constructor_string_parser(input, std::move(*token_list));
11508
11509
  // While parser's token index is less than parser's token list size:
11510
  while (parser.token_index < parser.token_list.size()) {
11511
    // Set parser's token increment to 1.
11512
    parser.token_increment = 1;
11513
11514
    // If parser's token list[parser's token index]'s type is "end" then:
11515
    if (parser.token_list[parser.token_index].type == token_type::END) {
11516
      // If parser's state is "init":
11517
      if (parser.state == State::INIT) {
11518
        // Run rewind given parser.
11519
        parser.rewind();
11520
        // If the result of running is a hash prefix given parser is true, then
11521
        // run change state given parser, "hash" and 1.
11522
        if (parser.is_hash_prefix()) {
11523
          parser.change_state(State::HASH, 1);
11524
        } else if (parser.is_search_prefix()) {
11525
          // Otherwise if the result of running is a search prefix given parser
11526
          // is true: Run change state given parser, "search" and 1.
11527
          parser.change_state(State::SEARCH, 1);
11528
        } else {
11529
          // Run change state given parser, "pathname" and 0.
11530
          parser.change_state(State::PATHNAME, 0);
11531
        }
11532
        // Increment parser's token index by parser's token increment.
11533
        parser.token_index += parser.token_increment;
11534
        // Continue.
11535
        continue;
11536
      }
11537
11538
      if (parser.state == State::AUTHORITY) {
11539
        // If parser's state is "authority":
11540
        // Run rewind and set state given parser, and "hostname".
11541
        parser.rewind();
11542
        parser.change_state(State::HOSTNAME, 0);
11543
        // Increment parser's token index by parser's token increment.
11544
        parser.token_index += parser.token_increment;
11545
        // Continue.
11546
        continue;
11547
      }
11548
11549
      // Run change state given parser, "done" and 0.
11550
      parser.change_state(State::DONE, 0);
11551
      // Break.
11552
      break;
11553
    }
11554
11555
    // If the result of running is a group open given parser is true:
11556
    if (parser.is_group_open()) {
11557
      // Increment parser's group depth by 1.
11558
      parser.group_depth += 1;
11559
      // Increment parser's token index by parser's token increment.
11560
      parser.token_index += parser.token_increment;
11561
      // Continue.
11562
      continue;
11563
    }
11564
11565
    // If parser's group depth is greater than 0:
11566
    if (parser.group_depth > 0) {
11567
      // If the result of running is a group close given parser is true, then
11568
      // decrement parser's group depth by 1.
11569
      if (parser.is_group_close()) {
11570
        parser.group_depth -= 1;
11571
      } else {
11572
        // Increment parser's token index by parser's token increment.
11573
        parser.token_index += parser.token_increment;
11574
        continue;
11575
      }
11576
    }
11577
11578
    // Switch on parser's state and run the associated steps:
11579
    switch (parser.state) {
11580
      case State::INIT: {
11581
        // If the result of running is a protocol suffix given parser is true:
11582
        if (parser.is_protocol_suffix()) {
11583
          // Run rewind and set state given parser and "protocol".
11584
          parser.rewind();
11585
          parser.change_state(State::PROTOCOL, 0);
11586
        }
11587
        break;
11588
      }
11589
      case State::PROTOCOL: {
11590
        // If the result of running is a protocol suffix given parser is true:
11591
        if (parser.is_protocol_suffix()) {
11592
          // Run compute protocol matches a special scheme flag given parser.
11593
          if (const auto error =
11594
                  parser.compute_protocol_matches_special_scheme_flag()) {
11595
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11596
            return tl::unexpected(*error);
11597
          }
11598
          // Let next state be "pathname".
11599
          auto next_state = State::PATHNAME;
11600
          // Let skip be 1.
11601
          auto skip = 1;
11602
          // If the result of running next is authority slashes given parser is
11603
          // true:
11604
          if (parser.next_is_authority_slashes()) {
11605
            // Set next state to "authority".
11606
            next_state = State::AUTHORITY;
11607
            // Set skip to 3.
11608
            skip = 3;
11609
          } else if (parser.protocol_matches_a_special_scheme_flag) {
11610
            // Otherwise if parser's protocol matches a special scheme flag is
11611
            // true, then set next state to "authority".
11612
            next_state = State::AUTHORITY;
11613
          }
11614
11615
          // Run change state given parser, next state, and skip.
11616
          parser.change_state(next_state, skip);
11617
        }
11618
        break;
11619
      }
11620
      case State::AUTHORITY: {
11621
        // If the result of running is an identity terminator given parser is
11622
        // true, then run rewind and set state given parser and "username".
11623
        if (parser.is_an_identity_terminator()) {
11624
          parser.rewind();
11625
          parser.change_state(State::USERNAME, 0);
11626
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11627
                   parser.is_hash_prefix()) {
11628
          // Otherwise if any of the following are true:
11629
          // - the result of running is a pathname start given parser;
11630
          // - the result of running is a search prefix given parser; or
11631
          // - the result of running is a hash prefix given parser,
11632
          // then run rewind and set state given parser and "hostname".
11633
          parser.rewind();
11634
          parser.change_state(State::HOSTNAME, 0);
11635
        }
11636
        break;
11637
      }
11638
      case State::USERNAME: {
11639
        // If the result of running is a password prefix given parser is true,
11640
        // then run change state given parser, "password", and 1.
11641
        if (parser.is_password_prefix()) {
11642
          parser.change_state(State::PASSWORD, 1);
11643
        } else if (parser.is_an_identity_terminator()) {
11644
          // Otherwise if the result of running is an identity terminator given
11645
          // parser is true, then run change state given parser, "hostname",
11646
          // and 1.
11647
          parser.change_state(State::HOSTNAME, 1);
11648
        }
11649
        break;
11650
      }
11651
      case State::PASSWORD: {
11652
        // If the result of running is an identity terminator given parser is
11653
        // true, then run change state given parser, "hostname", and 1.
11654
        if (parser.is_an_identity_terminator()) {
11655
          parser.change_state(State::HOSTNAME, 1);
11656
        }
11657
        break;
11658
      }
11659
      case State::HOSTNAME: {
11660
        // If the result of running is an IPv6 open given parser is true, then
11661
        // increment parser's hostname IPv6 bracket depth by 1.
11662
        if (parser.is_an_ipv6_open()) {
11663
          parser.hostname_ipv6_bracket_depth += 1;
11664
        } else if (parser.is_an_ipv6_close()) {
11665
          // Otherwise if the result of running is an IPv6 close given parser is
11666
          // true, then decrement parser's hostname IPv6 bracket depth by 1.
11667
          parser.hostname_ipv6_bracket_depth -= 1;
11668
        } else if (parser.is_port_prefix() &&
11669
                   parser.hostname_ipv6_bracket_depth == 0) {
11670
          // Otherwise if the result of running is a port prefix given parser is
11671
          // true and parser's hostname IPv6 bracket depth is zero, then run
11672
          // change state given parser, "port", and 1.
11673
          parser.change_state(State::PORT, 1);
11674
        } else if (parser.is_pathname_start()) {
11675
          // Otherwise if the result of running is a pathname start given parser
11676
          // is true, then run change state given parser, "pathname", and 0.
11677
          parser.change_state(State::PATHNAME, 0);
11678
        } else if (parser.is_search_prefix()) {
11679
          // Otherwise if the result of running is a search prefix given parser
11680
          // is true, then run change state given parser, "search", and 1.
11681
          parser.change_state(State::SEARCH, 1);
11682
        } else if (parser.is_hash_prefix()) {
11683
          // Otherwise if the result of running is a hash prefix given parser is
11684
          // true, then run change state given parser, "hash", and 1.
11685
          parser.change_state(State::HASH, 1);
11686
        }
11687
11688
        break;
11689
      }
11690
      case State::PORT: {
11691
        // If the result of running is a pathname start given parser is true,
11692
        // then run change state given parser, "pathname", and 0.
11693
        if (parser.is_pathname_start()) {
11694
          parser.change_state(State::PATHNAME, 0);
11695
        } else if (parser.is_search_prefix()) {
11696
          // Otherwise if the result of running is a search prefix given parser
11697
          // is true, then run change state given parser, "search", and 1.
11698
          parser.change_state(State::SEARCH, 1);
11699
        } else if (parser.is_hash_prefix()) {
11700
          // Otherwise if the result of running is a hash prefix given parser is
11701
          // true, then run change state given parser, "hash", and 1.
11702
          parser.change_state(State::HASH, 1);
11703
        }
11704
        break;
11705
      }
11706
      case State::PATHNAME: {
11707
        // If the result of running is a search prefix given parser is true,
11708
        // then run change state given parser, "search", and 1.
11709
        if (parser.is_search_prefix()) {
11710
          parser.change_state(State::SEARCH, 1);
11711
        } else if (parser.is_hash_prefix()) {
11712
          // Otherwise if the result of running is a hash prefix given parser is
11713
          // true, then run change state given parser, "hash", and 1.
11714
          parser.change_state(State::HASH, 1);
11715
        }
11716
        break;
11717
      }
11718
      case State::SEARCH: {
11719
        // If the result of running is a hash prefix given parser is true, then
11720
        // run change state given parser, "hash", and 1.
11721
        if (parser.is_hash_prefix()) {
11722
          parser.change_state(State::HASH, 1);
11723
        }
11724
        break;
11725
      }
11726
      case State::HASH: {
11727
        // Do nothing
11728
        break;
11729
      }
11730
      default: {
11731
        // Assert: This step is never reached.
11732
        unreachable();
11733
      }
11734
    }
11735
11736
    // Increment parser's token index by parser's token increment.
11737
    parser.token_index += parser.token_increment;
11738
  }
11739
11740
  // If parser's result contains "hostname" and not "port", then set parser's
11741
  // result["port"] to the empty string.
11742
  if (parser.result.hostname && !parser.result.port) {
11743
    parser.result.port = "";
11744
  }
11745
11746
  // Return parser's result.
11747
  return parser.result;
11748
}
11749
11750
}  // namespace ada::url_pattern_helpers
11751
#endif  // ADA_INCLUDE_URL_PATTERN
11752
#endif
11753
/* end file include/ada/url_pattern_helpers-inl.h */
11754
/* begin file include/ada/url_pattern_list.h */
11755
/**
11756
 * @file url_pattern_list.h
11757
 * @brief Compiled set-level matching over URLPattern pathname patterns.
11758
 *
11759
 * `ada::url_pattern_list` answers the question "which of my N routes matches
11760
 * this pathname, and what are the parameter values?" in a single walk
11761
 * instead of a loop of N `url_pattern::exec` calls. The whole route set is
11762
 * compiled once, by ada::parse_url_pattern_list, into a segment trie with
11763
 * per-node dispatch (direct compares for small fanouts, a first-byte index
11764
 * at the root, perfect-hash projection for wide nodes). Matching a pathname
11765
 * against the compiled set is allocation-free and does not execute any
11766
 * regular expression for routes written in the common static / ":param" /
11767
 * "*" subset; other routes are matched through the regex provider, exactly
11768
 * as ada::url_pattern does.
11769
 *
11770
 * This implements the route-set ("URLPatternList") use case discussed in
11771
 * https://github.com/whatwg/urlpattern/issues/166 for the pathname component.
11772
 *
11773
 * Correctness does not depend on the fast path: inputs or routes that exceed
11774
 * the fast-path limits (documented on the constants in
11775
 * `ada::url_pattern_list_limits`) are matched by a sequential fallback with
11776
 * identical priority semantics, never rejected and never answered wrongly.
11777
 *
11778
 * @see https://urlpattern.spec.whatwg.org/
11779
 */
11780
#ifndef ADA_URL_PATTERN_LIST_H
11781
#define ADA_URL_PATTERN_LIST_H
11782
11783
11784
#include <array>
11785
#include <cstdint>
11786
#include <optional>
11787
#include <span>
11788
#include <string>
11789
#include <string_view>
11790
#include <vector>
11791
11792
#if ADA_INCLUDE_URL_PATTERN
11793
namespace ada {
11794
11795
/**
11796
 * The limits of the url_pattern_list fast path. None of them changes what a
11797
 * list matches: routes and inputs beyond a limit are matched by a sequential
11798
 * fallback with identical semantics.
11799
 * @namespace ada::url_pattern_list_limits
11800
 */
11801
namespace url_pattern_list_limits {
11802
11803
/**
11804
 * Maximum number of captures (":param" groups plus one trailing "*" group)
11805
 * a route may declare and still be compiled into the fast path. Routes with
11806
 * more groups are matched by the sequential fallback; their match is still
11807
 * correct, but only the first `max_captures_per_route` captures are reported
11808
 * (see url_pattern_list_match_result::captures_truncated).
11809
 */
11810
inline constexpr uint32_t max_captures_per_route = 8;
11811
11812
/**
11813
 * Maximum number of '/'-separated segments an input pathname may have and
11814
 * still be matched by the fast path. Longer inputs fall back to the
11815
 * sequential matcher and are still matched correctly.
11816
 */
11817
inline constexpr uint32_t max_fast_path_segments = 24;
11818
11819
/**
11820
 * Maximum input pathname length (bytes) accepted by the fast path; segment
11821
 * offsets are tracked in 16-bit integers internally. Longer inputs fall back
11822
 * to the sequential matcher and are still matched correctly.
11823
 */
11824
inline constexpr uint32_t max_fast_path_pathname_length = 4096;
11825
11826
/**
11827
 * Maximum number of pattern segments a route may have and still be compiled
11828
 * into the trie fast path. Deeper routes are matched by the sequential
11829
 * fallback with identical semantics.
11830
 */
11831
inline constexpr uint32_t max_trie_pattern_segments = 16;
11832
11833
}  // namespace url_pattern_list_limits
11834
11835
/**
11836
 * The result of url_pattern_list::match. A winning route reports its group
11837
 * values in one of two forms, mirroring url_pattern_list::group_names():
11838
 *
11839
 * - Routes in the static / ":param" / "*" subset report zero-allocation
11840
 *   captures: (offset, length) slices of the pathname passed to match, in
11841
 *   capture order (":param" groups left to right, then the "*" group).
11842
 *   `regexp_route` is false and `regexp_groups` is empty.
11843
 * - Routes matched through the regex provider (patterns with custom regexp
11844
 *   groups or "?" / "+" / "*" modifiers) report the group values the
11845
 *   provider's regex_search returned, exactly as url_pattern::exec would for
11846
 *   the pathname component: `regexp_route` is true, `regexp_groups[i]` is the
11847
 *   value of group_names(route_index)[i] (nullopt for a group that did not
11848
 *   participate), and capture_count is 0.
11849
 *
11850
 * Routes with more than url_pattern_list_limits::max_captures_per_route
11851
 * groups report only the first max_captures_per_route slices and set
11852
 * captures_truncated.
11853
 */
11854
struct url_pattern_list_match_result {
11855
  /** One capture: an (offset, length) slice of the matched pathname. */
11856
  struct capture {
11857
    uint32_t offset = 0;
11858
    uint32_t length = 0;
11859
  };
11860
  /** Index of the winning route in the creation order, or -1 for no match. */
11861
  int32_t route_index = -1;
11862
  /** Number of valid entries in captures. */
11863
  uint32_t capture_count = 0;
11864
  /** True when the route has more groups than could be reported as slices. */
11865
  bool captures_truncated = false;
11866
  /** True when the winning route was matched through the regex provider; its
11867
   * group values are in regexp_groups. */
11868
  bool regexp_route = false;
11869
  std::array<capture, url_pattern_list_limits::max_captures_per_route>
11870
      captures{};
11871
  /** Group values of a regexp route, as returned by the provider. */
11872
  std::vector<std::optional<std::string>> regexp_groups{};
11873
11874
0
  [[nodiscard]] bool has_match() const noexcept { return route_index >= 0; }
11875
};
11876
11877
/**
11878
 * @private
11879
 * The compiled tables url_pattern_list::match walks, and the inline matcher
11880
 * over them (url_pattern_list-inl.h). Nothing here is part of the supported
11881
 * API. The route-set compiler that produces these tables is not part of the
11882
 * public headers at all (src/url_pattern_list_compiler.h).
11883
 * @namespace ada::url_pattern_list_detail
11884
 */
11885
namespace url_pattern_list_detail {
11886
11887
/** @private One trie node. Nodes with a projection dispatch keep their hash
11888
 * payload in a separate hash_record, so the common 0/1/direct nodes stay at
11889
 * 24 bytes. */
11890
struct node_record {
11891
  // First static edge (edges are laid out per node, contiguously).
11892
  int32_t first_child = 0;
11893
  // Child node index, -2 - route (pure-leaf shortcut), or -1.
11894
  int32_t param_child = -1;
11895
  // Route index of a "*" ending at this prefix, or -1.
11896
  int32_t wild_route = -1;
11897
  // Route ending exactly here, or -1.
11898
  int32_t terminal_route = -1;
11899
  uint16_t n_static = 0;
11900
  // 0 none, 1 direct compares, 2 projection, 3 linear scan, 4 first-byte
11901
  // index (root only).
11902
  uint8_t dispatch = 0;
11903
  // 1 if the node still has a param/wildcard alternative to backtrack to.
11904
  uint8_t has_alternative = 0;
11905
  // Index of the node's hash_record (dispatch == 2 only).
11906
  uint32_t hash_index = 0;
11907
};
11908
11909
/** @private Projection-dispatch payload of a hashed node. */
11910
struct hash_record {
11911
  uint64_t multiplier = 0;
11912
  uint32_t slot_base = 0;
11913
  // Witness plan: 3 nibble ids | count << 12 | use_len << 14.
11914
  uint16_t witness_plan = 0;
11915
  // Slot table size == 1 << slot_bits.
11916
  uint8_t slot_bits = 0;
11917
  uint8_t reserved = 0;
11918
};
11919
11920
/** @private One static edge; keys <= 16 bytes verify from the packed
11921
 * windows alone. */
11922
struct edge_record {
11923
  // Key bytes [0, min(8, len)), little-endian, zero-padded.
11924
  uint64_t prefix = 0;
11925
  // Key bytes [max(0, len - 8), len), little-endian, zero-padded.
11926
  uint64_t suffix = 0;
11927
  // Child node index; negative values encode -2 - route pure-leaf shortcuts.
11928
  int32_t node = -1;
11929
  // Full key in the blob (read only when key_length > 16).
11930
  uint32_t key_offset = 0;
11931
  uint16_t key_length = 0;
11932
};
11933
11934
/** @private How one route of the set is matched. */
11935
enum class route_mode : uint8_t {
11936
  // Compiled into the trie fast path.
11937
  trie = 0,
11938
  // Safe static/":param"/"*" syntax beyond a fast-path build limit: matched
11939
  // by the sequential segment matcher.
11940
  sequential = 1,
11941
  // Needs URLPattern regexp semantics: matched through the regex provider.
11942
  regexp = 2,
11943
};
11944
11945
/** @private Everything the matcher knows about one route after a hit. */
11946
struct route_record {
11947
  // Packed per-segment kinds, two bits per segment, most significant first;
11948
  // (kind_sequence, kind_length, route index) compared as a tuple is the
11949
  // specificity order.
11950
  uint64_t kind_sequence = 0;
11951
  // Sequential-mode routes: their segments in the segment table.
11952
  uint32_t segment_first = 0;
11953
  uint32_t segment_count = 0;
11954
  // Auxiliary routes that can outrank this route when it wins the fast
11955
  // path: a range of the aux table (usually empty).
11956
  uint32_t challenger_first = 0;
11957
  uint32_t challenger_count = 0;
11958
  // Ordinal among the regexp-mode routes, or -1.
11959
  int32_t regexp_component = -1;
11960
  uint8_t kind_length = 0;
11961
  route_mode mode = route_mode::sequential;
11962
  // Trie-mode routes: capture positions.
11963
  uint8_t n_params = 0;
11964
  uint8_t wild = 0;
11965
  std::array<uint8_t, url_pattern_list_limits::max_captures_per_route>
11966
      param_positions{};
11967
};
11968
11969
/** @private One pattern segment of a sequential-mode route. */
11970
struct segment_record {
11971
  uint32_t text_offset = 0;
11972
  uint32_t text_length = 0;
11973
  // 0 literal, 1 ":param", 2 "*".
11974
  uint8_t kind = 0;
11975
};
11976
11977
/**
11978
 * @private
11979
 * The compiled route set: one arena holding every table, addressed by
11980
 * section offsets, plus the per-route group names. Sections are 8-byte
11981
 * aligned; the blob is zero-padded by 8 bytes so short key windows can be
11982
 * loaded whole.
11983
 */
11984
struct compiled_routes {
11985
  std::vector<uint8_t> arena{};
11986
  std::vector<std::vector<std::string>> group_names{};
11987
  uint32_t nodes_offset = 0;
11988
  uint32_t hashes_offset = 0;
11989
  uint32_t edges_offset = 0;
11990
  uint32_t slots_offset = 0;
11991
  uint32_t blob_offset = 0;
11992
  uint32_t routes_offset = 0;
11993
  uint32_t segments_offset = 0;
11994
  uint32_t aux_offset = 0;
11995
  uint32_t root_index_offset = 0;
11996
  uint32_t n_routes = 0;
11997
  // All auxiliary routes (regexp mode and sequential mode) in insertion
11998
  // order: the aux table's first n_aux_all entries; challenger ranges
11999
  // follow.
12000
  uint32_t n_aux_all = 0;
12001
  uint8_t ignore_case = 0;
12002
12003
  template <typename T>
12004
0
  [[nodiscard]] const T* section(uint32_t offset) const noexcept {
12005
0
    return reinterpret_cast<const T*>(arena.data() + offset);
12006
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
12007
};
12008
12009
/** @private Result of the fast-path engine or of the sequential matcher. */
12010
struct engine_result {
12011
  using capture = url_pattern_list_match_result::capture;
12012
  int32_t route = -1;
12013
  uint32_t capture_count = 0;
12014
  // False when the input exceeded a fast-path limit and the caller must run
12015
  // the sequential fallback over the whole route set.
12016
  bool within_fast_path = true;
12017
  bool captures_truncated = false;
12018
  std::array<capture, url_pattern_list_limits::max_captures_per_route>
12019
      captures{};
12020
};
12021
12022
/**
12023
 * @private
12024
 * Parses and compiles a set of pathname patterns (already base-URL
12025
 * processed). Routes that need the regex provider come back with
12026
 * route_record::regexp_component set to their ordinal in insertion order;
12027
 * the caller compiles those components and fills their group names.
12028
 */
12029
tl::expected<compiled_routes, errors> compile_pathname_patterns(
12030
    std::span<const std::string> patterns, bool ignore_case);
12031
12032
/**
12033
 * @private
12034
 * Sequential reference matcher for one non-regexp route of the compiled set;
12035
 * matches any input (no fast-path limits) with the same semantics as the
12036
 * compiled engine. With `fold_input`, input bytes are ASCII case-folded on
12037
 * the fly (the compiled literals are already folded). On a match, fills
12038
 * `result` (route index is NOT set) and returns true.
12039
 */
12040
bool match_route_sequential(const compiled_routes& tables, uint32_t route,
12041
                            std::string_view pathname, bool fold_input,
12042
                            engine_result& result) noexcept;
12043
12044
/**
12045
 * @private
12046
 * True when the bytes [p, p + n) hold no line terminator. A "*" segment
12047
 * compiles to "(.*)" in the URLPattern regexp, and "." in an ECMAScript
12048
 * regular expression does not match LF or CR, so a wildcard may not capture
12049
 * past one. Deliberately not inlined into the walk (it costs the static and
12050
 * ":param" paths, which never run it), and deliberately defined in
12051
 * url_pattern_list.cpp rather than here: MSVC's ada_never_inline carries no
12052
 * inline linkage, so a definition in this header is emitted in every
12053
 * translation unit.
12054
 */
12055
bool wildcard_tail_ok(const char* p, uint32_t n) noexcept;
12056
12057
/**
12058
 * @private
12059
 * Cheap pre-check for a regexp-mode route before running the provider: the
12060
 * part of the route's segment shape that is certain (see the compiler's
12061
 * approximate_kind_sequence) must fit the pathname -- its anchored literal
12062
 * segments, and its exact segment count when every group is a ":name"
12063
 * segment wildcard. False proves the regular expression cannot match.
12064
 */
12065
bool match_regexp_shape(const compiled_routes& tables, uint32_t route,
12066
                        std::string_view pathname, bool fold_input) noexcept;
12067
12068
/**
12069
 * @private
12070
 * True when route `a` (at insertion index `a_index`) outranks route `b` (at
12071
 * `b_index`): lexicographically smaller kind sequence, insertion order as the
12072
 * tiebreak.
12073
 */
12074
constexpr bool outranks(uint64_t a_sequence, uint32_t a_length, size_t a_index,
12075
                        uint64_t b_sequence, uint32_t b_length,
12076
0
                        size_t b_index) noexcept {
12077
0
  if (a_sequence != b_sequence) {
12078
0
    return a_sequence < b_sequence;
12079
0
  }
12080
0
  if (a_length != b_length) {
12081
0
    return a_length < b_length;
12082
0
  }
12083
0
  return a_index < b_index;
12084
0
}
12085
12086
}  // namespace url_pattern_list_detail
12087
12088
template <url_pattern_regex::regex_concept regex_provider>
12089
class url_pattern_list;
12090
12091
/**
12092
 * @private
12093
 * url_pattern_list befriends these two specializations, so they have to be
12094
 * declared before it: a friend declaration that redeclares the function
12095
 * template instead is not matched by every compiler we support (Apple clang
12096
 * 15 rejects the resulting member access). The documented declarations are
12097
 * in implementation.h, next to parse_url_pattern, and carry the default
12098
 * arguments: this header includes url_pattern.h, which includes
12099
 * implementation.h, so those declarations are always seen first, and a
12100
 * function template may not gain default arguments in a later declaration.
12101
 */
12102
template <url_pattern_regex::regex_concept regex_provider>
12103
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12104
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
12105
                       const std::string_view* base_url,
12106
                       const url_pattern_options* options);
12107
12108
/** @private See above. */
12109
template <url_pattern_regex::regex_concept regex_provider>
12110
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12111
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns);
12112
12113
/**
12114
 * @brief A compiled set of URLPattern pathname patterns with one-shot
12115
 * matching.
12116
 *
12117
 * Build once with ada::parse_url_pattern_list, then call match() per
12118
 * request. Matching within the fast-path limits is allocation-free and
12119
 * regex-free for routes written in the static / ":param" / "*" subset; other
12120
 * routes are matched through the regex provider (the same regex_search call
12121
 * url_pattern::exec makes) and still participate in the priority order.
12122
 *
12123
 * Match priority is specificity order, not registration order: routes are
12124
 * compared by their per-segment kind sequence (literal < ":param" < "*",
12125
 * lexicographically from the first segment), with insertion order breaking
12126
 * ties. This is the priority scheme of find-my-way (Fastify) and Express-
12127
 * style routers. Whether a standardized URLPatternList should instead use
12128
 * pure first-match-in-insertion-order semantics is an open question; the
12129
 * compiled representation supports either, and the priority rule is
12130
 * deliberately centralized in url_pattern_list_detail::outranks.
12131
 *
12132
 * Scope (v1): the pathname component only. Other URL components are treated
12133
 * as fully wildcarded; match() takes an already-extracted pathname (for
12134
 * example ada::url_aggregator::get_pathname()). Inputs are matched as given
12135
 * and are expected to be in canonical (percent-encoded) form; the pattern
12136
 * side is canonicalized by the URLPattern pattern parser at creation. The
12137
 * subset follows the URLPattern regexp it stands for: a ":param" segment
12138
 * ("[^/]+?") is one non-empty segment, and a "*" tail ("(.*)") matches any
12139
 * bytes except the line terminators LF and CR, which "." never matches.
12140
 *
12141
 * With url_pattern_options::ignore_case, regexp routes are compiled through
12142
 * the provider with the flag set (as url_pattern does), and the static /
12143
 * ":param" / "*" subset compares literal segments with ASCII case folding,
12144
 * which is what a case-insensitive regular expression does over the ASCII
12145
 * text of a canonical pathname.
12146
 *
12147
 * @tparam regex_provider The regex implementation used only for routes that
12148
 *         need URLPattern regexp semantics. Must satisfy
12149
 *         url_pattern_regex::regex_concept.
12150
 */
12151
template <url_pattern_regex::regex_concept regex_provider>
12152
class url_pattern_list {
12153
 public:
12154
  url_pattern_list() = default;
12155
12156
  /**
12157
   * Matches a pathname against the set and returns the winning route with
12158
   * its captures (see url_pattern_list_match_result). The fast path is
12159
   * allocation-free; inputs beyond the fast-path limits are matched by the
12160
   * sequential fallback with identical semantics.
12161
   */
12162
  [[nodiscard]] url_pattern_list_match_result match(
12163
      std::string_view pathname) const;
12164
12165
  /** Number of routes in the set. */
12166
  [[nodiscard]] size_t size() const noexcept { return patterns_.size(); }
12167
12168
  /** The pathname pattern string the route at `route_index` was created
12169
   * from (after base URL processing, if a base URL was given). */
12170
  [[nodiscard]] std::string_view pattern(size_t route_index) const
12171
      ada_lifetime_bound {
12172
    return patterns_[route_index];
12173
  }
12174
12175
  /**
12176
   * Capture group names of the route at `route_index`, in the order of
12177
   * url_pattern_list_match_result::captures / regexp_groups ("*" groups have
12178
   * numeric names, per URLPattern).
12179
   */
12180
  [[nodiscard]] const std::vector<std::string>& group_names(
12181
      size_t route_index) const ada_lifetime_bound {
12182
    return compiled_.group_names[route_index];
12183
  }
12184
12185
  /** The ignore_case option the set was created with. */
12186
  [[nodiscard]] bool ignore_case() const noexcept {
12187
    return compiled_.ignore_case != 0;
12188
  }
12189
12190
  friend tl::expected<url_pattern_list, errors>
12191
  parse_url_pattern_list<regex_provider>(
12192
      std::span<const std::string_view> pathname_patterns,
12193
      const std::string_view* base_url, const url_pattern_options* options);
12194
12195
  friend tl::expected<url_pattern_list, errors> parse_url_pattern_list<
12196
      regex_provider>(std::span<const url_pattern<regex_provider>> patterns);
12197
12198
 private:
12199
  /** @private Compiles the (already processed) pattern strings; the regexp
12200
   * components are then supplied by the caller in insertion order. */
12201
  static tl::expected<url_pattern_list, errors> create(
12202
      std::vector<std::string>&& pathname_patterns, bool ignore_case);
12203
12204
  /** @private Tests one auxiliary route (sequential or regexp mode) and,
12205
   * when it matches and outranks `best`, replaces `best` with it. `probe`
12206
   * is the (possibly case-folded) pathname for the sequential matcher,
12207
   * `pathname` the original for the provider. */
12208
  void consider_route(
12209
      uint32_t route_index, std::string_view pathname, std::string_view probe,
12210
      bool fold_input, url_pattern_list_detail::engine_result& best,
12211
      std::vector<std::optional<std::string>>& best_groups) const;
12212
12213
  /** @private The pattern strings, by route index. */
12214
  std::vector<std::string> patterns_{};
12215
  /** @private The compiled tables. */
12216
  url_pattern_list_detail::compiled_routes compiled_{};
12217
  /** @private Compiled pathname components of the regexp-mode routes, in
12218
   * insertion order. */
12219
  std::vector<url_pattern_component<regex_provider>> regexp_components_{};
12220
};
12221
12222
}  // namespace ada
12223
#endif  // ADA_INCLUDE_URL_PATTERN
12224
#endif  // ADA_URL_PATTERN_LIST_H
12225
/* end file include/ada/url_pattern_list.h */
12226
/* begin file include/ada/url_pattern_list-inl.h */
12227
/**
12228
 * @file url_pattern_list-inl.h
12229
 * @brief The url_pattern_list matcher (inline) and the class template's
12230
 * member definitions.
12231
 *
12232
 * The walk over the compiled tables lives here, as ada_really_inline
12233
 * functions, so that it inlines into url_pattern_list::match; the route-set
12234
 * compiler that builds the tables is in its own translation unit
12235
 * (src/url_pattern_list.cpp) and is not part of the public headers.
12236
 */
12237
#ifndef ADA_URL_PATTERN_LIST_INL_H
12238
#define ADA_URL_PATTERN_LIST_INL_H
12239
12240
12241
#include <bit>
12242
#include <cstring>
12243
#include <optional>
12244
#include <string>
12245
#include <string_view>
12246
#include <utility>
12247
#include <vector>
12248
12249
#if ADA_NEON
12250
#include <arm_neon.h>
12251
#endif
12252
12253
#if ADA_INCLUDE_URL_PATTERN
12254
namespace ada::url_pattern_list_detail {
12255
12256
// ---- byte helpers ----------------------------------------------------------
12257
12258
// Unaligned 8-byte little-endian load; all 8 bytes must be readable. The
12259
// byte-assembly fallback keeps big-endian targets correct (the compiler
12260
// lowers it to a single load plus byte swap).
12261
0
ada_really_inline uint64_t load8_le(const char* p) noexcept {
12262
0
  if constexpr (std::endian::native == std::endian::little) {
12263
0
    uint64_t x;
12264
0
    std::memcpy(&x, p, 8);
12265
0
    return x;
12266
  } else {
12267
    uint64_t x = 0;
12268
    for (uint32_t j = 0; j < 8; j++) {
12269
      x |= static_cast<uint64_t>(static_cast<uint8_t>(p[j])) << (8 * j);
12270
    }
12271
    return x;
12272
  }
12273
0
}
12274
12275
// Bytes [0, n) of p packed little-endian, zero-padded; reads exactly n
12276
// bytes (n < 8). This is the byte-compare path for short tails.
12277
0
ada_really_inline uint64_t gather_le(const char* p, uint32_t n) noexcept {
12278
0
  uint64_t x = 0;
12279
0
  for (uint32_t j = 0; j < n; j++) {
12280
0
    x |= static_cast<uint64_t>(static_cast<uint8_t>(p[j])) << (8 * j);
12281
0
  }
12282
0
  return x;
12283
0
}
12284
12285
// Mask selecting the low n bytes, 1 <= n <= 7.
12286
0
constexpr uint64_t low_bytes_mask(uint32_t n) noexcept {
12287
0
  return ~0ull >> (64 - 8 * n);
12288
0
}
12289
12290
// ASCII case folding of n bytes from src into dst (the compiler vectorizes
12291
// this loop). Only ASCII letters change: canonical pathnames are ASCII, and
12292
// this is what a case-insensitive regular expression does over them.
12293
0
inline void ascii_fold(const char* src, uint32_t n, char* dst) noexcept {
12294
0
  for (uint32_t i = 0; i < n; i++) {
12295
0
    const uint8_t c = static_cast<uint8_t>(src[i]);
12296
0
    dst[i] = static_cast<char>(c | ((c >= 'A' && c <= 'Z') ? 0x20u : 0u));
12297
0
  }
12298
0
}
12299
12300
// ---- witness machinery -----------------------------------------------------
12301
// Projection dispatch for wide trie nodes. A witness id names a feature of a
12302
// byte string:
12303
//   0..7   the byte at absolute offset id           (0 when out of range)
12304
//   8..15  the byte at offset id-8 counted from the end
12305
// A projection is three witness bytes plus the length packed into one
12306
// uint64; a perfect multiplier turns it into a slot index. Builder and
12307
// matcher call the same accessors and the same packing, so a slot table
12308
// cannot disagree with the gather that reads it.
12309
12310
0
constexpr uint8_t length_byte(uint32_t len) noexcept {
12311
0
  return len < 255u ? static_cast<uint8_t>(len) : static_cast<uint8_t>(255);
12312
0
}
12313
12314
constexpr uint8_t witness_byte(const char* p, uint32_t len,
12315
0
                               uint8_t id) noexcept {  // ids 0..15
12316
0
  uint32_t o = id & 7u;
12317
0
  bool in = o < len;
12318
0
  uint32_t pos = (id & 8u) ? len - 1u - o : o;  // wraps when !in; masked then
12319
0
  pos = in ? pos : 0u;
12320
0
  uint8_t v = len ? static_cast<uint8_t>(p[pos]) : static_cast<uint8_t>(0);
12321
0
  return in ? v : static_cast<uint8_t>(0);
12322
0
}
12323
12324
constexpr uint64_t pack_projection(uint8_t f0, uint8_t f1, uint8_t f2,
12325
0
                                   uint8_t f3) noexcept {
12326
0
  return (static_cast<uint64_t>(f0) << 24) | (static_cast<uint64_t>(f1) << 16) |
12327
0
         (static_cast<uint64_t>(f2) << 8) | static_cast<uint64_t>(f3);
12328
0
}
12329
12330
// Perfect-multiplier slot index; b <= 12 everywhere, so the shift is in
12331
// range and the product needs no modulus.
12332
constexpr uint64_t slot_of(uint64_t proj, uint64_t multiplier,
12333
0
                           uint8_t b) noexcept {
12334
0
  return (proj * multiplier) >> (64 - b);
12335
0
}
12336
12337
// Fixed-shape projection of one string: always 3 witness bytes plus the
12338
// length. Duplicated ids add no information but keep the gather uniform.
12339
constexpr uint64_t project(const char* p, uint32_t len,
12340
0
                           uint16_t plan) noexcept {
12341
0
  return pack_projection(
12342
0
      witness_byte(p, len, static_cast<uint8_t>(plan & 15u)),
12343
0
      witness_byte(p, len, static_cast<uint8_t>((plan >> 4) & 15u)),
12344
0
      witness_byte(p, len, static_cast<uint8_t>((plan >> 8) & 15u)),
12345
0
      length_byte(len));
12346
0
}
12347
12348
// ---- segment scan ----------------------------------------------------------
12349
12350
// Splits the pathname into segments in one pass, in place: only segment
12351
// starts are recorded, with a sentinel soff[nseg] = ulen + 1 so that
12352
// seg_len(i) == soff[i + 1] - soff[i] - 1. Returns the segment count, or 0
12353
// when the input has more than max_fast_path_segments segments (the caller
12354
// falls back to the sequential matcher). Requires ulen >= 1 and
12355
// url[0] == '/'.
12356
//
12357
// Two implementations with the same contract: the portable SWAR scan (8
12358
// bytes per step, an exact zero-byte test on x ^ '/'-fill with no false
12359
// positives, bytes >= 0x80 included, and a byte-exact partial load for the
12360
// tail, so the input is never over-read), and on AArch64 a NEON scan for
12361
// inputs of 16 bytes or more (16 bytes per step, the '/' compare narrowed
12362
// to one nibble per byte, the last block overlapping the previous one so
12363
// the input is never over-read). The 1k+ pathname benchmark in the PR
12364
// discussion is what keeps the NEON scan: it is faster at every length and
12365
// 3-4x faster past 1 KB.
12366
struct segment_emitter {
12367
  uint16_t* soff;
12368
  uint32_t nseg = 0;
12369
  uint32_t start = 1;
12370
  // One segment start per set bit of `lanes`; a lane is 1 << shift bits
12371
  // wide (8 for the SWAR masks, 4 for the NEON nibble masks).
12372
  ada_really_inline bool emit(uint64_t lanes, uint32_t base,
12373
0
                              unsigned shift) noexcept {
12374
0
    using url_pattern_list_limits::max_fast_path_segments;
12375
0
    while (lanes) {
12376
0
      const uint32_t pos =
12377
0
          base + (static_cast<uint32_t>(std::countr_zero(lanes)) >> shift);
12378
0
      if (nseg >= max_fast_path_segments) {
12379
0
        return false;
12380
0
      }
12381
0
      soff[nseg++] = static_cast<uint16_t>(start);
12382
0
      start = pos + 1;
12383
0
      lanes &= lanes - 1;
12384
0
    }
12385
0
    return true;
12386
0
  }
12387
0
  ada_really_inline uint32_t finish(uint32_t ulen) noexcept {
12388
0
    using url_pattern_list_limits::max_fast_path_segments;
12389
0
    if (nseg >= max_fast_path_segments) {
12390
0
      return 0;
12391
0
    }
12392
0
    soff[nseg++] = static_cast<uint16_t>(start);
12393
0
    soff[nseg] = static_cast<uint16_t>(ulen + 1);  // sentinel
12394
0
    return nseg;
12395
0
  }
12396
};
12397
12398
ada_really_inline uint32_t scan_segments_swar(const char* url, uint32_t ulen,
12399
0
                                              uint16_t* soff) noexcept {
12400
0
  constexpr uint64_t slashes = 0x2F2F2F2F2F2F2F2Full;
12401
0
  constexpr uint64_t low7 = 0x7F7F7F7F7F7F7F7Full;
12402
0
  // Bit 7 of every byte of x that is zero, exactly: (b & 0x7F) + 0x7F has
12403
0
  // bit 7 set iff the low bits are non-zero, b itself has it set iff b is
12404
0
  // >= 0x80, and no lane can carry into its neighbour.
12405
0
  const auto zero_lanes = [](uint64_t x) noexcept {
12406
0
    return ~(((x & low7) + low7) | x | low7);
12407
0
  };
12408
0
  segment_emitter out{soff};
12409
0
  uint32_t i = 1;
12410
0
  for (; i + 8 <= ulen; i += 8) {
12411
0
    const uint64_t lanes = zero_lanes(load8_le(url + i) ^ slashes);
12412
0
    if (lanes != 0 && !out.emit(lanes, i, 3)) {
12413
0
      return 0;
12414
0
    }
12415
0
  }
12416
0
  if (i < ulen) {  // tail of 1..7 bytes: zero padding is never a '/'
12417
0
    const uint64_t lanes = zero_lanes(gather_le(url + i, ulen - i) ^ slashes);
12418
0
    if (lanes != 0 && !out.emit(lanes, i, 3)) {
12419
0
      return 0;
12420
0
    }
12421
0
  }
12422
0
  return out.finish(ulen);
12423
0
}
12424
12425
#if ADA_NEON
12426
ada_really_inline uint32_t scan_segments_neon(const char* url, uint32_t ulen,
12427
                                              uint16_t* soff) noexcept {
12428
  // Requires ulen >= 16.
12429
  const uint8x16_t slash = vdupq_n_u8('/');
12430
  // One bit per byte lane that holds a '/': compare, narrow each 16-bit
12431
  // pair to its high nibble, keep bit 0 of every nibble.
12432
  const auto slash_lanes = [&](const char* p) noexcept {
12433
    const uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
12434
    const uint64_t nibbles =
12435
        vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(
12436
                          vreinterpretq_u16_u8(vceqq_u8(v, slash)), 4)),
12437
                      0);
12438
    return nibbles & 0x1111111111111111ull;
12439
  };
12440
  segment_emitter out{soff};
12441
  uint32_t i = 0;
12442
  for (; i + 16 <= ulen; i += 16) {
12443
    uint64_t lanes = slash_lanes(url + i);
12444
    if (i == 0) {
12445
      lanes &= ~0xFull;  // the leading '/' is not a separator
12446
    }
12447
    if (lanes != 0 && !out.emit(lanes, i, 2)) {
12448
      return 0;
12449
    }
12450
  }
12451
  if (i < ulen) {  // overlapped last block: drop the lanes already scanned
12452
    const uint32_t off = ulen - 16;
12453
    const uint64_t lanes = slash_lanes(url + off) & (~0ull << ((i - off) * 4));
12454
    if (lanes != 0 && !out.emit(lanes, off, 2)) {
12455
      return 0;
12456
    }
12457
  }
12458
  return out.finish(ulen);
12459
}
12460
#endif  // ADA_NEON
12461
12462
ada_really_inline uint32_t scan_segments(const char* url, uint32_t ulen,
12463
0
                                         uint16_t* soff) noexcept {
12464
0
#if ADA_NEON
12465
0
  if (ulen >= 16) {
12466
0
    return scan_segments_neon(url, ulen, soff);
12467
0
  }
12468
0
#endif
12469
0
  return scan_segments_swar(url, ulen, soff);
12470
0
}
12471
12472
// ---- verify / dispatch -----------------------------------------------------
12473
12474
// One input segment prepared for dispatch: its bytes, its length, and how
12475
// many bytes are readable from p (to the end of the pathname).
12476
struct segment_ref {
12477
  const char* p;
12478
  uint32_t len;
12479
  uint32_t avail;
12480
};
12481
12482
// Segment-vs-edge compare. Keys of 8..16 bytes verify from two whole-word
12483
// loads inside the segment; shorter keys from one masked load when the
12484
// input has 8 readable bytes, else from a byte gather (short tails such as
12485
// "42" or "me"); longer keys through memcmp against the blob. An empty key
12486
// (a pattern such as "/users/") matches exactly the empty segment.
12487
ada_really_inline bool verify_edge(const edge_record& e, const segment_ref& s,
12488
0
                                   const char* blob) noexcept {
12489
0
  const uint32_t len = s.len;
12490
0
  if (len != e.key_length) {
12491
0
    return false;
12492
0
  }
12493
0
  if (len == 0) {
12494
0
    return true;
12495
0
  }
12496
0
  if (len > 16) {
12497
0
    return std::memcmp(s.p, blob + e.key_offset, len) == 0;
12498
0
  }
12499
0
  if (len >= 8) {
12500
0
    return ((load8_le(s.p) ^ e.prefix) |
12501
0
            (load8_le(s.p + len - 8) ^ e.suffix)) == 0;
12502
0
  }
12503
0
  const uint64_t x = s.avail >= 8 ? (load8_le(s.p) & low_bytes_mask(len))
12504
0
                                  : gather_le(s.p, len);
12505
0
  return x == e.prefix;
12506
0
}
12507
12508
// The typed view of the arena the walk reads; derived once per match.
12509
struct table_view {
12510
  const node_record* nodes;
12511
  const hash_record* hashes;
12512
  const edge_record* edges;
12513
  const uint8_t* slots;
12514
  const char* blob;
12515
  const uint16_t* root_index;
12516
};
12517
12518
0
ada_really_inline table_view view_of(const compiled_routes& r) noexcept {
12519
0
  return table_view{r.section<node_record>(r.nodes_offset),
12520
0
                    r.section<hash_record>(r.hashes_offset),
12521
0
                    r.section<edge_record>(r.edges_offset),
12522
0
                    r.section<uint8_t>(r.slots_offset),
12523
0
                    r.section<char>(r.blob_offset),
12524
0
                    r.section<uint16_t>(r.root_index_offset)};
12525
0
}
12526
12527
// Static-child dispatch: returns the child ordinal within the node or -1.
12528
// Correctness never depends on the projection or the index: the candidate
12529
// is always confirmed by a full segment compare, so a bad table can only
12530
// cost time, never change an answer.
12531
ada_really_inline int32_t dispatch_static(const table_view& t,
12532
                                          const node_record& nd,
12533
0
                                          const segment_ref& s) noexcept {
12534
0
  const edge_record* e = t.edges + nd.first_child;
12535
0
  switch (nd.dispatch) {
12536
0
    case 0:
12537
0
      return -1;
12538
0
    case 1: {  // direct: up to 8 compares, each gated by the key length
12539
0
      for (uint32_t j = 0; j < nd.n_static; j++) {
12540
0
        if (verify_edge(e[j], s, t.blob)) {
12541
0
          return static_cast<int32_t>(j);
12542
0
        }
12543
0
      }
12544
0
      return -1;
12545
0
    }
12546
0
    case 2: {  // projection: gather -> multiply -> slot -> verify
12547
0
      const hash_record& h = t.hashes[nd.hash_index];
12548
0
      const uint64_t proj = project(s.p, s.len, h.witness_plan);
12549
0
      const uint8_t ord =
12550
0
          t.slots[h.slot_base + slot_of(proj, h.multiplier, h.slot_bits)];
12551
0
      if (ord == 0xFF) {
12552
0
        return -1;
12553
0
      }
12554
0
      return verify_edge(e[ord], s, t.blob) ? static_cast<int32_t>(ord) : -1;
12555
0
    }
12556
0
    case 4: {  // root first-byte index: jump to the run of children that
12557
0
               // share the segment's first byte (children are sorted by it)
12558
0
      if (s.len == 0) {
12559
0
        return -1;  // no first byte; an indexed root has no empty key
12560
0
      }
12561
0
      const uint8_t b = static_cast<uint8_t>(s.p[0]);
12562
0
      uint32_t j = t.root_index[b];
12563
0
      if (j == 0xFFFF) {
12564
0
        return -1;
12565
0
      }
12566
0
      for (; j < nd.n_static && static_cast<uint8_t>(e[j].prefix & 0xFFu) == b;
12567
0
           j++) {
12568
0
        if (verify_edge(e[j], s, t.blob)) {
12569
0
          return static_cast<int32_t>(j);
12570
0
        }
12571
0
      }
12572
0
      return -1;
12573
0
    }
12574
0
    default: {  // linear demotion rung (also carries fanout > 254)
12575
0
      for (uint32_t j = 0; j < nd.n_static; j++) {
12576
0
        if (verify_edge(e[j], s, t.blob)) {
12577
0
          return static_cast<int32_t>(j);
12578
0
        }
12579
0
      }
12580
0
      return -1;
12581
0
    }
12582
0
  }
12583
0
}
12584
12585
// ---- the match residual ----------------------------------------------------
12586
12587
// Matches `pathname` against the compiled tables. When the input is out of
12588
// the fast-path contract, returns with within_fast_path == false and no
12589
// answer; the caller falls back to the sequential matcher.
12590
ada_really_inline void match_compiled(const compiled_routes& r, const char* url,
12591
                                      uint32_t ulen,
12592
0
                                      engine_result& out) noexcept {
12593
0
  using url_pattern_list_limits::max_fast_path_pathname_length;
12594
0
  using url_pattern_list_limits::max_fast_path_segments;
12595
0
  out = engine_result{};
12596
0
  if (ulen == 0 || url[0] != '/' || ulen > max_fast_path_pathname_length) {
12597
0
    out.within_fast_path = false;  // the sequential fallback decides
12598
0
    return;
12599
0
  }
12600
0
  const table_view t = view_of(r);
12601
0
  const route_record* routes = r.section<route_record>(r.routes_offset);
12602
0
12603
0
  // Segment scan; overflow means the input is beyond the fast path.
12604
0
  uint16_t soff[max_fast_path_segments + 1];
12605
0
  const uint32_t nseg = scan_segments(url, ulen, soff);
12606
0
  if (nseg == 0) {
12607
0
    out.within_fast_path = false;
12608
0
    return;
12609
0
  }
12610
0
  const auto seg_len = [&](uint32_t i) {
12611
0
    return static_cast<uint32_t>(soff[i + 1]) - soff[i] - 1u;
12612
0
  };
12613
0
  const auto seg_ref = [&](uint32_t i) {
12614
0
    const uint32_t off = soff[i];
12615
0
    return segment_ref{url + off, seg_len(i), ulen - off};
12616
0
  };
12617
0
12618
0
  // Trie walk with an explicit bounded backtrack stack. Every node offers
12619
0
  // the same three alternatives in the compiled priority order (static
12620
0
  // child, param child, wildcard); a backtrack record stores the resume
12621
0
  // point, and a record is pushed only when the node still has an untried
12622
0
  // alternative, so the stack never exceeds the walk depth. Edges encoded
12623
0
  // as -2 - route are pure-leaf shortcuts.
12624
0
  enum alternative : uint16_t {
12625
0
    alt_static = 0,
12626
0
    alt_param = 1,
12627
0
    alt_wild = 2,
12628
0
    alt_none = 3
12629
0
  };
12630
0
  struct backtrack {
12631
0
    int32_t node;
12632
0
    uint16_t i;
12633
0
    uint16_t resume;
12634
0
  };
12635
0
  backtrack stack[max_fast_path_segments + 1];
12636
0
  uint32_t sp = 0;
12637
0
  int32_t node = 0;
12638
0
  uint32_t i = 0;
12639
0
  uint32_t alt = alt_static;
12640
0
  int32_t route = -1;
12641
0
  uint32_t wild_from = 0;
12642
0
  bool wild = false;
12643
0
  for (;;) {
12644
0
    const node_record& nd = t.nodes[static_cast<size_t>(node)];
12645
0
    if (alt == alt_static) {
12646
0
      if (i == nseg) {
12647
0
        if (nd.terminal_route >= 0) {
12648
0
          route = nd.terminal_route;
12649
0
          break;
12650
0
        }
12651
0
        alt = alt_none;
12652
0
      } else {
12653
0
        const int32_t ord = dispatch_static(t, nd, seg_ref(i));
12654
0
        if (ord >= 0) {
12655
0
          const int32_t nxt = t.edges[static_cast<size_t>(nd.first_child) +
12656
0
                                      static_cast<size_t>(ord)]
12657
0
                                  .node;
12658
0
          if (nxt >= 0) {
12659
0
            if (nd.has_alternative) {
12660
0
              stack[sp++] = {node, static_cast<uint16_t>(i), alt_param};
12661
0
            }
12662
0
            node = nxt;
12663
0
            i++;
12664
0
            continue;
12665
0
          }
12666
0
          // Pure-leaf shortcut.
12667
0
          if (i + 1 == nseg) {
12668
0
            route = -2 - nxt;
12669
0
            break;
12670
0
          }
12671
0
          alt = alt_param;  // dead end: fall through to this node's fallbacks
12672
0
        } else {
12673
0
          alt = alt_param;
12674
0
        }
12675
0
      }
12676
0
    }
12677
0
    if (alt == alt_param) {
12678
0
      const int32_t pc = nd.param_child;
12679
0
      if (pc != -1 && seg_len(i) > 0) {
12680
0
        if (pc >= 0) {
12681
0
          if (nd.wild_route >= 0) {
12682
0
            stack[sp++] = {node, static_cast<uint16_t>(i), alt_wild};
12683
0
          }
12684
0
          node = pc;
12685
0
          i++;
12686
0
          alt = alt_static;
12687
0
          continue;
12688
0
        }
12689
0
        // Pure-leaf param shortcut.
12690
0
        if (i + 1 == nseg) {
12691
0
          route = -2 - pc;
12692
0
          break;
12693
0
        }
12694
0
        alt = alt_wild;  // only the wildcard remains here
12695
0
      } else {
12696
0
        alt = alt_wild;
12697
0
      }
12698
0
    }
12699
0
    if (alt == alt_wild) {
12700
0
      if (nd.wild_route >= 0 &&
12701
0
          wildcard_tail_ok(url + soff[i], ulen - soff[i])) {
12702
0
        route = nd.wild_route;
12703
0
        wild_from = i;
12704
0
        wild = true;
12705
0
        break;
12706
0
      }
12707
0
      // No alternative left at this node: resume from the backtrack stack
12708
0
      // (alt is overwritten by the popped record).
12709
0
    }
12710
0
    if (sp == 0) {
12711
0
      break;  // nothing to resume: miss
12712
0
    }
12713
0
    --sp;
12714
0
    node = stack[sp].node;
12715
0
    i = stack[sp].i;
12716
0
    alt = stack[sp].resume;
12717
0
  }
12718
0
12719
0
  out.route = route;
12720
0
  if (route >= 0) {  // capture extraction: params first, wildcard tail last
12721
0
    const route_record& rm = routes[static_cast<size_t>(route)];
12722
0
    uint32_t nc = 0;
12723
0
    for (uint32_t k = 0; k < rm.n_params; k++) {
12724
0
      const uint32_t p = rm.param_positions[k];
12725
0
      out.captures[nc++] = {soff[p], seg_len(p)};
12726
0
    }
12727
0
    if (wild) {
12728
0
      out.captures[nc++] = {soff[wild_from], ulen - soff[wild_from]};
12729
0
    }
12730
0
    out.capture_count = nc;
12731
0
  }
12732
0
}
12733
12734
}  // namespace ada::url_pattern_list_detail
12735
12736
namespace ada {
12737
12738
template <url_pattern_regex::regex_concept regex_provider>
12739
tl::expected<url_pattern_list<regex_provider>, errors>
12740
url_pattern_list<regex_provider>::create(
12741
    std::vector<std::string>&& pathname_patterns, bool ignore_case) {
12742
  url_pattern_list list{};
12743
  list.patterns_ = std::move(pathname_patterns);
12744
  auto compiled = url_pattern_list_detail::compile_pathname_patterns(
12745
      list.patterns_, ignore_case);
12746
  if (!compiled) {
12747
    return tl::unexpected(compiled.error());
12748
  }
12749
  list.compiled_ = std::move(*compiled);
12750
  return list;
12751
}
12752
12753
template <url_pattern_regex::regex_concept regex_provider>
12754
void url_pattern_list<regex_provider>::consider_route(
12755
    uint32_t route_index, std::string_view pathname, std::string_view probe,
12756
    bool fold_input, url_pattern_list_detail::engine_result& best,
12757
    std::vector<std::optional<std::string>>& best_groups) const {
12758
  namespace detail = url_pattern_list_detail;
12759
  const detail::route_record* routes =
12760
      compiled_.section<detail::route_record>(compiled_.routes_offset);
12761
  const detail::route_record& candidate = routes[route_index];
12762
  // Only test candidates that would outrank the current best.
12763
  if (best.route >= 0) {
12764
    const detail::route_record& current =
12765
        routes[static_cast<size_t>(best.route)];
12766
    if (!detail::outranks(candidate.kind_sequence, candidate.kind_length,
12767
                          route_index, current.kind_sequence,
12768
                          current.kind_length,
12769
                          static_cast<size_t>(best.route))) {
12770
      return;
12771
    }
12772
  }
12773
  if (candidate.mode == detail::route_mode::regexp) {
12774
    // A route whose certain segment shape does not fit the input cannot
12775
    // match at all: skip the provider.
12776
    if (!detail::match_regexp_shape(compiled_, route_index, probe,
12777
                                    fold_input)) {
12778
      return;
12779
    }
12780
    // URLPattern's own test/exec split: regex_match answers yes/no (this is
12781
    // what rejects a non-matching route cheaply; a regex_search miss can
12782
    // cost a scan over every start position), and regex_search then
12783
    // produces the group values exactly as url_pattern::exec does.
12784
    const url_pattern_component<regex_provider>& component =
12785
        regexp_components_[static_cast<size_t>(candidate.regexp_component)];
12786
    if (!component.fast_test(pathname)) {
12787
      return;
12788
    }
12789
    auto groups = component.fast_match(pathname);
12790
    if (groups) {
12791
      best = detail::engine_result{};
12792
      best.route = static_cast<int32_t>(route_index);
12793
      best_groups = std::move(*groups);
12794
    }
12795
  } else {
12796
    detail::engine_result scratch{};
12797
    if (detail::match_route_sequential(compiled_, route_index, probe,
12798
                                       fold_input, scratch)) {
12799
      scratch.route = static_cast<int32_t>(route_index);
12800
      best = scratch;
12801
      best_groups.clear();
12802
    }
12803
  }
12804
}
12805
12806
template <url_pattern_regex::regex_concept regex_provider>
12807
url_pattern_list_match_result url_pattern_list<regex_provider>::match(
12808
    std::string_view pathname) const {
12809
  namespace detail = url_pattern_list_detail;
12810
  using url_pattern_list_limits::max_fast_path_pathname_length;
12811
  // With ignore_case the compiled literals are ASCII-folded, so the fast
12812
  // path probes a folded copy of the input (offsets are unchanged, so the
12813
  // captures still slice the original). Inputs beyond the fast-path length
12814
  // are folded on the fly by the sequential matcher instead.
12815
  char folded[max_fast_path_pathname_length];
12816
  std::string_view probe = pathname;
12817
  bool fold_input = false;
12818
  if (compiled_.ignore_case) {
12819
    if (pathname.size() <= max_fast_path_pathname_length) {
12820
      detail::ascii_fold(pathname.data(),
12821
                         static_cast<uint32_t>(pathname.size()), folded);
12822
      probe = std::string_view(folded, pathname.size());
12823
    } else {
12824
      fold_input = true;
12825
    }
12826
  }
12827
  detail::engine_result best{};
12828
  std::vector<std::optional<std::string>> best_groups{};
12829
  detail::match_compiled(compiled_, probe.data(),
12830
                         static_cast<uint32_t>(probe.size()), best);
12831
  const uint32_t* aux = compiled_.section<uint32_t>(compiled_.aux_offset);
12832
  if (!best.within_fast_path) {
12833
    // The input exceeds a fast-path limit (length, segment count, or no
12834
    // leading '/'): the sequential fallback matches every route with
12835
    // identical priority semantics.
12836
    best = detail::engine_result{};
12837
    for (size_t i = 0; i < patterns_.size(); i++) {
12838
      consider_route(static_cast<uint32_t>(i), pathname, probe, fold_input,
12839
                     best, best_groups);
12840
    }
12841
  } else if (best.route >= 0) {
12842
    // A fast-path winner is final except for the auxiliary routes recorded
12843
    // at creation as able to outrank it (usually none).
12844
    const detail::route_record& winner =
12845
        compiled_.section<detail::route_record>(
12846
            compiled_.routes_offset)[static_cast<size_t>(best.route)];
12847
    for (uint32_t k = 0; k < winner.challenger_count; k++) {
12848
      consider_route(aux[winner.challenger_first + k], pathname, probe,
12849
                     fold_input, best, best_groups);
12850
    }
12851
  } else {
12852
    // A fast-path miss: any auxiliary route may still match.
12853
    for (uint32_t k = 0; k < compiled_.n_aux_all; k++) {
12854
      consider_route(aux[k], pathname, probe, fold_input, best, best_groups);
12855
    }
12856
  }
12857
  url_pattern_list_match_result result{};
12858
  result.route_index = best.route;
12859
  if (best.route >= 0) {
12860
    const detail::route_record& winner =
12861
        compiled_.section<detail::route_record>(
12862
            compiled_.routes_offset)[static_cast<size_t>(best.route)];
12863
    if (winner.mode == detail::route_mode::regexp) {
12864
      result.regexp_route = true;
12865
      result.regexp_groups = std::move(best_groups);
12866
    } else {
12867
      result.capture_count = best.capture_count;
12868
      result.captures_truncated = best.captures_truncated;
12869
      result.captures = best.captures;
12870
    }
12871
  }
12872
  return result;
12873
}
12874
12875
}  // namespace ada
12876
#endif  // ADA_INCLUDE_URL_PATTERN
12877
#endif  // ADA_URL_PATTERN_LIST_INL_H
12878
/* end file include/ada/url_pattern_list-inl.h */
12879
12880
// Public API
12881
/* begin file include/ada/ada_version.h */
12882
/**
12883
 * @file ada_version.h
12884
 * @brief Definitions for Ada's version number.
12885
 */
12886
#ifndef ADA_ADA_VERSION_H
12887
#define ADA_ADA_VERSION_H
12888
12889
0
#define ADA_VERSION "4.0.0"
12890
12891
namespace ada {
12892
12893
enum {
12894
  ADA_VERSION_MAJOR = 4,
12895
  ADA_VERSION_MINOR = 0,
12896
  ADA_VERSION_REVISION = 0,
12897
};
12898
12899
}  // namespace ada
12900
12901
#endif  // ADA_ADA_VERSION_H
12902
/* end file include/ada/ada_version.h */
12903
/* begin file include/ada/implementation-inl.h */
12904
/**
12905
 * @file implementation-inl.h
12906
 */
12907
#ifndef ADA_IMPLEMENTATION_INL_H
12908
#define ADA_IMPLEMENTATION_INL_H
12909
12910
12911
12912
#include <variant>
12913
#include <string_view>
12914
12915
namespace ada {
12916
12917
#if ADA_INCLUDE_URL_PATTERN
12918
template <url_pattern_regex::regex_concept regex_provider>
12919
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
12920
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
12921
                  const std::string_view* base_url,
12922
                  const url_pattern_options* options) {
12923
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
12924
                                                        base_url, options);
12925
}
12926
12927
template <url_pattern_regex::regex_concept regex_provider>
12928
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12929
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
12930
                       const std::string_view* base_url,
12931
                       const url_pattern_options* options) {
12932
  std::vector<std::string> processed;
12933
  processed.reserve(pathname_patterns.size());
12934
  for (const std::string_view pattern : pathname_patterns) {
12935
    if (base_url == nullptr) {
12936
      processed.emplace_back(pattern);
12937
      continue;
12938
    }
12939
    // With a base URL, each pattern is processed exactly as the pathname of
12940
    // a URLPatternInit would be: a relative pattern is resolved against the
12941
    // base URL's path (and an unparsable base URL is a type error).
12942
    url_pattern_init init{};
12943
    init.pathname = std::string(pattern);
12944
    init.base_url = std::string(*base_url);
12945
    auto result = url_pattern_init::process(
12946
        init, url_pattern_init::process_type::pattern);
12947
    if (!result) {
12948
      return tl::unexpected(result.error());
12949
    }
12950
    processed.push_back(std::move(result->pathname).value_or(std::string{}));
12951
  }
12952
  const bool ignore_case = options != nullptr && options->ignore_case;
12953
  auto list = url_pattern_list<regex_provider>::create(std::move(processed),
12954
                                                       ignore_case);
12955
  if (!list) {
12956
    return list;
12957
  }
12958
  // Routes outside the static/":param"/"*" subset are compiled as a
12959
  // URLPattern pathname component through the provider, with the same
12960
  // options parse_url_pattern would use (ignore_case included).
12961
  auto compile_options = url_pattern_compile_component_options::PATHNAME;
12962
  compile_options.ignore_case = ignore_case;
12963
  const url_pattern_list_detail::route_record* routes =
12964
      list->compiled_.template section<url_pattern_list_detail::route_record>(
12965
          list->compiled_.routes_offset);
12966
  for (size_t i = 0; i < list->patterns_.size(); i++) {
12967
    if (routes[i].regexp_component < 0) {
12968
      continue;
12969
    }
12970
    auto component = url_pattern_component<regex_provider>::compile(
12971
        list->patterns_[i], url_pattern_helpers::canonicalize_pathname,
12972
        compile_options);
12973
    if (!component) {
12974
      return tl::unexpected(component.error());
12975
    }
12976
    list->compiled_.group_names[i] = component->group_name_list;
12977
    list->regexp_components_.push_back(std::move(*component));
12978
  }
12979
  return list;
12980
}
12981
12982
template <url_pattern_regex::regex_concept regex_provider>
12983
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12984
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns) {
12985
  std::vector<std::string> texts;
12986
  texts.reserve(patterns.size());
12987
  const bool ignore_case = !patterns.empty() && patterns[0].ignore_case();
12988
  for (const url_pattern<regex_provider>& pattern : patterns) {
12989
    if (pattern.ignore_case() != ignore_case) {
12990
      return tl::unexpected(errors::type_error);  // one flag per list
12991
    }
12992
    texts.emplace_back(pattern.get_pathname());
12993
  }
12994
  auto list =
12995
      url_pattern_list<regex_provider>::create(std::move(texts), ignore_case);
12996
  if (!list) {
12997
    return list;
12998
  }
12999
  // Routes outside the subset reuse the pattern's already compiled pathname
13000
  // component: no second create_instance through the provider.
13001
  const url_pattern_list_detail::route_record* routes =
13002
      list->compiled_.template section<url_pattern_list_detail::route_record>(
13003
          list->compiled_.routes_offset);
13004
  for (size_t i = 0; i < patterns.size(); i++) {
13005
    if (routes[i].regexp_component < 0) {
13006
      continue;
13007
    }
13008
    list->compiled_.group_names[i] =
13009
        patterns[i].pathname_component.group_name_list;
13010
    list->regexp_components_.push_back(patterns[i].pathname_component);
13011
  }
13012
  return list;
13013
}
13014
#endif  // ADA_INCLUDE_URL_PATTERN
13015
13016
}  // namespace ada
13017
13018
#endif  // ADA_IMPLEMENTATION_INL_H
13019
/* end file include/ada/implementation-inl.h */
13020
13021
#endif  // ADA_H
13022
/* end file include/ada.h */