Coverage Report

Created: 2026-09-03 06:31

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-02 20:28:58 -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
#endif  // ADA_COMMON_DEFS_H
544
/* end file include/ada/common_defs.h */
545
#include <cstdint>
546
547
/**
548
 * These functions are not part of our public API and may
549
 * change at any time.
550
 * @private
551
 * @namespace ada::character_sets
552
 * @brief Includes the definitions for unicode character sets.
553
 */
554
namespace ada::character_sets {
555
ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i);
556
}  // namespace ada::character_sets
557
558
#endif  // ADA_CHARACTER_SETS_H
559
/* end file include/ada/character_sets.h */
560
/* begin file include/ada/character_sets-inl.h */
561
/**
562
 * @file character_sets-inl.h
563
 * @brief Definitions of the character sets used by unicode functions.
564
 * @author Node.js
565
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
566
 */
567
#ifndef ADA_CHARACTER_SETS_INL_H
568
#define ADA_CHARACTER_SETS_INL_H
569
570
571
/**
572
 * These functions are not part of our public API and may
573
 * change at any time.
574
 * @private
575
 */
576
namespace ada::character_sets {
577
578
constexpr char hex[1024] =
579
    "%00\0%01\0%02\0%03\0%04\0%05\0%06\0%07\0"
580
    "%08\0%09\0%0A\0%0B\0%0C\0%0D\0%0E\0%0F\0"
581
    "%10\0%11\0%12\0%13\0%14\0%15\0%16\0%17\0"
582
    "%18\0%19\0%1A\0%1B\0%1C\0%1D\0%1E\0%1F\0"
583
    "%20\0%21\0%22\0%23\0%24\0%25\0%26\0%27\0"
584
    "%28\0%29\0%2A\0%2B\0%2C\0%2D\0%2E\0%2F\0"
585
    "%30\0%31\0%32\0%33\0%34\0%35\0%36\0%37\0"
586
    "%38\0%39\0%3A\0%3B\0%3C\0%3D\0%3E\0%3F\0"
587
    "%40\0%41\0%42\0%43\0%44\0%45\0%46\0%47\0"
588
    "%48\0%49\0%4A\0%4B\0%4C\0%4D\0%4E\0%4F\0"
589
    "%50\0%51\0%52\0%53\0%54\0%55\0%56\0%57\0"
590
    "%58\0%59\0%5A\0%5B\0%5C\0%5D\0%5E\0%5F\0"
591
    "%60\0%61\0%62\0%63\0%64\0%65\0%66\0%67\0"
592
    "%68\0%69\0%6A\0%6B\0%6C\0%6D\0%6E\0%6F\0"
593
    "%70\0%71\0%72\0%73\0%74\0%75\0%76\0%77\0"
594
    "%78\0%79\0%7A\0%7B\0%7C\0%7D\0%7E\0%7F\0"
595
    "%80\0%81\0%82\0%83\0%84\0%85\0%86\0%87\0"
596
    "%88\0%89\0%8A\0%8B\0%8C\0%8D\0%8E\0%8F\0"
597
    "%90\0%91\0%92\0%93\0%94\0%95\0%96\0%97\0"
598
    "%98\0%99\0%9A\0%9B\0%9C\0%9D\0%9E\0%9F\0"
599
    "%A0\0%A1\0%A2\0%A3\0%A4\0%A5\0%A6\0%A7\0"
600
    "%A8\0%A9\0%AA\0%AB\0%AC\0%AD\0%AE\0%AF\0"
601
    "%B0\0%B1\0%B2\0%B3\0%B4\0%B5\0%B6\0%B7\0"
602
    "%B8\0%B9\0%BA\0%BB\0%BC\0%BD\0%BE\0%BF\0"
603
    "%C0\0%C1\0%C2\0%C3\0%C4\0%C5\0%C6\0%C7\0"
604
    "%C8\0%C9\0%CA\0%CB\0%CC\0%CD\0%CE\0%CF\0"
605
    "%D0\0%D1\0%D2\0%D3\0%D4\0%D5\0%D6\0%D7\0"
606
    "%D8\0%D9\0%DA\0%DB\0%DC\0%DD\0%DE\0%DF\0"
607
    "%E0\0%E1\0%E2\0%E3\0%E4\0%E5\0%E6\0%E7\0"
608
    "%E8\0%E9\0%EA\0%EB\0%EC\0%ED\0%EE\0%EF\0"
609
    "%F0\0%F1\0%F2\0%F3\0%F4\0%F5\0%F6\0%F7\0"
610
    "%F8\0%F9\0%FA\0%FB\0%FC\0%FD\0%FE\0%FF";
611
612
constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32] = {
613
    // 00     01     02     03     04     05     06     07
614
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
615
    // 08     09     0A     0B     0C     0D     0E     0F
616
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
617
    // 10     11     12     13     14     15     16     17
618
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
619
    // 18     19     1A     1B     1C     1D     1E     1F
620
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
621
    // 20     21     22     23     24     25     26     27
622
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
623
    // 28     29     2A     2B     2C     2D     2E     2F
624
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
625
    // 30     31     32     33     34     35     36     37
626
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
627
    // 38     39     3A     3B     3C     3D     3E     3F
628
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
629
    // 40     41     42     43     44     45     46     47
630
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
631
    // 48     49     4A     4B     4C     4D     4E     4F
632
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
633
    // 50     51     52     53     54     55     56     57
634
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
635
    // 58     59     5A     5B     5C     5D     5E     5F
636
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
637
    // 60     61     62     63     64     65     66     67
638
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
639
    // 68     69     6A     6B     6C     6D     6E     6F
640
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
641
    // 70     71     72     73     74     75     76     77
642
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
643
    // 78     79     7A     7B     7C     7D     7E     7F
644
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
645
    // 80     81     82     83     84     85     86     87
646
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
647
    // 88     89     8A     8B     8C     8D     8E     8F
648
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
649
    // 90     91     92     93     94     95     96     97
650
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
651
    // 98     99     9A     9B     9C     9D     9E     9F
652
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
653
    // A0     A1     A2     A3     A4     A5     A6     A7
654
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
655
    // A8     A9     AA     AB     AC     AD     AE     AF
656
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
657
    // B0     B1     B2     B3     B4     B5     B6     B7
658
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
659
    // B8     B9     BA     BB     BC     BD     BE     BF
660
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
661
    // C0     C1     C2     C3     C4     C5     C6     C7
662
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
663
    // C8     C9     CA     CB     CC     CD     CE     CF
664
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
665
    // D0     D1     D2     D3     D4     D5     D6     D7
666
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
667
    // D8     D9     DA     DB     DC     DD     DE     DF
668
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
669
    // E0     E1     E2     E3     E4     E5     E6     E7
670
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
671
    // E8     E9     EA     EB     EC     ED     EE     EF
672
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
673
    // F0     F1     F2     F3     F4     F5     F6     F7
674
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
675
    // F8     F9     FA     FB     FC     FD     FE     FF
676
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
677
678
constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32] = {
679
    // 00     01     02     03     04     05     06     07
680
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
681
    // 08     09     0A     0B     0C     0D     0E     0F
682
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
683
    // 10     11     12     13     14     15     16     17
684
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
685
    // 18     19     1A     1B     1C     1D     1E     1F
686
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
687
    // 20     21     22     23     24     25     26     27
688
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x80,
689
    // 28     29     2A     2B     2C     2D     2E     2F
690
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
691
    // 30     31     32     33     34     35     36     37
692
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
693
    // 38     39     3A     3B     3C     3D     3E     3F
694
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
695
    // 40     41     42     43     44     45     46     47
696
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
697
    // 48     49     4A     4B     4C     4D     4E     4F
698
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
699
    // 50     51     52     53     54     55     56     57
700
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
701
    // 58     59     5A     5B     5C     5D     5E     5F
702
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
703
    // 60     61     62     63     64     65     66     67
704
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
705
    // 68     69     6A     6B     6C     6D     6E     6F
706
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
707
    // 70     71     72     73     74     75     76     77
708
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
709
    // 78     79     7A     7B     7C     7D     7E     7F
710
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
711
    // 80     81     82     83     84     85     86     87
712
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
713
    // 88     89     8A     8B     8C     8D     8E     8F
714
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
715
    // 90     91     92     93     94     95     96     97
716
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
717
    // 98     99     9A     9B     9C     9D     9E     9F
718
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
719
    // A0     A1     A2     A3     A4     A5     A6     A7
720
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
721
    // A8     A9     AA     AB     AC     AD     AE     AF
722
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
723
    // B0     B1     B2     B3     B4     B5     B6     B7
724
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
725
    // B8     B9     BA     BB     BC     BD     BE     BF
726
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
727
    // C0     C1     C2     C3     C4     C5     C6     C7
728
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
729
    // C8     C9     CA     CB     CC     CD     CE     CF
730
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
731
    // D0     D1     D2     D3     D4     D5     D6     D7
732
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
733
    // D8     D9     DA     DB     DC     DD     DE     DF
734
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
735
    // E0     E1     E2     E3     E4     E5     E6     E7
736
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
737
    // E8     E9     EA     EB     EC     ED     EE     EF
738
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
739
    // F0     F1     F2     F3     F4     F5     F6     F7
740
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
741
    // F8     F9     FA     FB     FC     FD     FE     FF
742
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
743
744
constexpr uint8_t QUERY_PERCENT_ENCODE[32] = {
745
    // 00     01     02     03     04     05     06     07
746
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
747
    // 08     09     0A     0B     0C     0D     0E     0F
748
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
749
    // 10     11     12     13     14     15     16     17
750
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
751
    // 18     19     1A     1B     1C     1D     1E     1F
752
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
753
    // 20     21     22     23     24     25     26     27
754
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
755
    // 28     29     2A     2B     2C     2D     2E     2F
756
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
757
    // 30     31     32     33     34     35     36     37
758
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
759
    // 38     39     3A     3B     3C     3D     3E     3F
760
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
761
    // 40     41     42     43     44     45     46     47
762
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
763
    // 48     49     4A     4B     4C     4D     4E     4F
764
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
765
    // 50     51     52     53     54     55     56     57
766
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
767
    // 58     59     5A     5B     5C     5D     5E     5F
768
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
769
    // 60     61     62     63     64     65     66     67
770
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
771
    // 68     69     6A     6B     6C     6D     6E     6F
772
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
773
    // 70     71     72     73     74     75     76     77
774
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
775
    // 78     79     7A     7B     7C     7D     7E     7F
776
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
777
    // 80     81     82     83     84     85     86     87
778
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
779
    // 88     89     8A     8B     8C     8D     8E     8F
780
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
781
    // 90     91     92     93     94     95     96     97
782
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
783
    // 98     99     9A     9B     9C     9D     9E     9F
784
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
785
    // A0     A1     A2     A3     A4     A5     A6     A7
786
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
787
    // A8     A9     AA     AB     AC     AD     AE     AF
788
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
789
    // B0     B1     B2     B3     B4     B5     B6     B7
790
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
791
    // B8     B9     BA     BB     BC     BD     BE     BF
792
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
793
    // C0     C1     C2     C3     C4     C5     C6     C7
794
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
795
    // C8     C9     CA     CB     CC     CD     CE     CF
796
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
797
    // D0     D1     D2     D3     D4     D5     D6     D7
798
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
799
    // D8     D9     DA     DB     DC     DD     DE     DF
800
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
801
    // E0     E1     E2     E3     E4     E5     E6     E7
802
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
803
    // E8     E9     EA     EB     EC     ED     EE     EF
804
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
805
    // F0     F1     F2     F3     F4     F5     F6     F7
806
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
807
    // F8     F9     FA     FB     FC     FD     FE     FF
808
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
809
810
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32] = {
811
    // 00     01     02     03     04     05     06     07
812
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
813
    // 08     09     0A     0B     0C     0D     0E     0F
814
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
815
    // 10     11     12     13     14     15     16     17
816
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
817
    // 18     19     1A     1B     1C     1D     1E     1F
818
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
819
    // 20     21     22     23     24     25     26     27
820
    0x01 | 0x00 | 0x04 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
821
    // 28     29     2A     2B     2C     2D     2E     2F
822
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
823
    // 30     31     32     33     34     35     36     37
824
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
825
    // 38     39     3A     3B     3C     3D     3E     3F
826
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
827
    // 40     41     42     43     44     45     46     47
828
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
829
    // 48     49     4A     4B     4C     4D     4E     4F
830
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
831
    // 50     51     52     53     54     55     56     57
832
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
833
    // 58     59     5A     5B     5C     5D     5E     5F
834
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
835
    // 60     61     62     63     64     65     66     67
836
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
837
    // 68     69     6A     6B     6C     6D     6E     6F
838
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
839
    // 70     71     72     73     74     75     76     77
840
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
841
    // 78     79     7A     7B     7C     7D     7E     7F
842
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
843
    // 80     81     82     83     84     85     86     87
844
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
845
    // 88     89     8A     8B     8C     8D     8E     8F
846
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
847
    // 90     91     92     93     94     95     96     97
848
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
849
    // 98     99     9A     9B     9C     9D     9E     9F
850
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
851
    // A0     A1     A2     A3     A4     A5     A6     A7
852
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
853
    // A8     A9     AA     AB     AC     AD     AE     AF
854
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
855
    // B0     B1     B2     B3     B4     B5     B6     B7
856
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
857
    // B8     B9     BA     BB     BC     BD     BE     BF
858
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
859
    // C0     C1     C2     C3     C4     C5     C6     C7
860
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
861
    // C8     C9     CA     CB     CC     CD     CE     CF
862
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
863
    // D0     D1     D2     D3     D4     D5     D6     D7
864
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
865
    // D8     D9     DA     DB     DC     DD     DE     DF
866
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
867
    // E0     E1     E2     E3     E4     E5     E6     E7
868
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
869
    // E8     E9     EA     EB     EC     ED     EE     EF
870
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
871
    // F0     F1     F2     F3     F4     F5     F6     F7
872
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
873
    // F8     F9     FA     FB     FC     FD     FE     FF
874
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
875
876
constexpr uint8_t USERINFO_PERCENT_ENCODE[32] = {
877
    // 00     01     02     03     04     05     06     07
878
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
879
    // 08     09     0A     0B     0C     0D     0E     0F
880
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
881
    // 10     11     12     13     14     15     16     17
882
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
883
    // 18     19     1A     1B     1C     1D     1E     1F
884
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
885
    // 20     21     22     23     24     25     26     27
886
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
887
    // 28     29     2A     2B     2C     2D     2E     2F
888
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
889
    // 30     31     32     33     34     35     36     37
890
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
891
    // 38     39     3A     3B     3C     3D     3E     3F
892
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
893
    // 40     41     42     43     44     45     46     47
894
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
895
    // 48     49     4A     4B     4C     4D     4E     4F
896
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
897
    // 50     51     52     53     54     55     56     57
898
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
899
    // 58     59     5A     5B     5C     5D     5E     5F
900
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
901
    // 60     61     62     63     64     65     66     67
902
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
903
    // 68     69     6A     6B     6C     6D     6E     6F
904
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
905
    // 70     71     72     73     74     75     76     77
906
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
907
    // 78     79     7A     7B     7C     7D     7E     7F
908
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80,
909
    // 80     81     82     83     84     85     86     87
910
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
911
    // 88     89     8A     8B     8C     8D     8E     8F
912
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
913
    // 90     91     92     93     94     95     96     97
914
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
915
    // 98     99     9A     9B     9C     9D     9E     9F
916
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
917
    // A0     A1     A2     A3     A4     A5     A6     A7
918
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
919
    // A8     A9     AA     AB     AC     AD     AE     AF
920
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
921
    // B0     B1     B2     B3     B4     B5     B6     B7
922
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
923
    // B8     B9     BA     BB     BC     BD     BE     BF
924
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
925
    // C0     C1     C2     C3     C4     C5     C6     C7
926
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
927
    // C8     C9     CA     CB     CC     CD     CE     CF
928
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
929
    // D0     D1     D2     D3     D4     D5     D6     D7
930
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
931
    // D8     D9     DA     DB     DC     DD     DE     DF
932
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
933
    // E0     E1     E2     E3     E4     E5     E6     E7
934
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
935
    // E8     E9     EA     EB     EC     ED     EE     EF
936
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
937
    // F0     F1     F2     F3     F4     F5     F6     F7
938
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
939
    // F8     F9     FA     FB     FC     FD     FE     FF
940
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
941
942
constexpr uint8_t PATH_PERCENT_ENCODE[32] = {
943
    // 00     01     02     03     04     05     06     07
944
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
945
    // 08     09     0A     0B     0C     0D     0E     0F
946
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
947
    // 10     11     12     13     14     15     16     17
948
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
949
    // 18     19     1A     1B     1C     1D     1E     1F
950
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
951
    // 20     21     22     23     24     25     26     27
952
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
953
    // 28     29     2A     2B     2C     2D     2E     2F
954
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
955
    // 30     31     32     33     34     35     36     37
956
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
957
    // 38     39     3A     3B     3C     3D     3E     3F
958
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
959
    // 40     41     42     43     44     45     46     47
960
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
961
    // 48     49     4A     4B     4C     4D     4E     4F
962
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
963
    // 50     51     52     53     54     55     56     57
964
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
965
    // 58     59     5A     5B     5C     5D     5E     5F
966
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x40 | 0x00,
967
    // 60     61     62     63     64     65     66     67
968
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
969
    // 68     69     6A     6B     6C     6D     6E     6F
970
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
971
    // 70     71     72     73     74     75     76     77
972
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
973
    // 78     79     7A     7B     7C     7D     7E     7F
974
    0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80,
975
    // 80     81     82     83     84     85     86     87
976
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
977
    // 88     89     8A     8B     8C     8D     8E     8F
978
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
979
    // 90     91     92     93     94     95     96     97
980
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
981
    // 98     99     9A     9B     9C     9D     9E     9F
982
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
983
    // A0     A1     A2     A3     A4     A5     A6     A7
984
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
985
    // A8     A9     AA     AB     AC     AD     AE     AF
986
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
987
    // B0     B1     B2     B3     B4     B5     B6     B7
988
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
989
    // B8     B9     BA     BB     BC     BD     BE     BF
990
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
991
    // C0     C1     C2     C3     C4     C5     C6     C7
992
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
993
    // C8     C9     CA     CB     CC     CD     CE     CF
994
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
995
    // D0     D1     D2     D3     D4     D5     D6     D7
996
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
997
    // D8     D9     DA     DB     DC     DD     DE     DF
998
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
999
    // E0     E1     E2     E3     E4     E5     E6     E7
1000
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1001
    // E8     E9     EA     EB     EC     ED     EE     EF
1002
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1003
    // F0     F1     F2     F3     F4     F5     F6     F7
1004
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1005
    // F8     F9     FA     FB     FC     FD     FE     FF
1006
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1007
1008
constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = {
1009
    // 00     01     02     03     04     05     06     07
1010
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1011
    // 08     09     0A     0B     0C     0D     0E     0F
1012
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1013
    // 10     11     12     13     14     15     16     17
1014
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1015
    // 18     19     1A     1B     1C     1D     1E     1F
1016
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1017
    // 20     21     22     23     24     25     26     27
1018
    0x00 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1019
    // 28     29     2A     2B     2C     2D     2E     2F
1020
    0x01 | 0x02 | 0x00 | 0x08 | 0x10 | 0x00 | 0x00 | 0x80,
1021
    // 30     31     32     33     34     35     36     37
1022
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1023
    // 38     39     3A     3B     3C     3D     3E     3F
1024
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1025
    // 40     41     42     43     44     45     46     47
1026
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1027
    // 48     49     4A     4B     4C     4D     4E     4F
1028
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1029
    // 50     51     52     53     54     55     56     57
1030
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1031
    // 58     59     5A     5B     5C     5D     5E     5F
1032
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
1033
    // 60     61     62     63     64     65     66     67
1034
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1035
    // 68     69     6A     6B     6C     6D     6E     6F
1036
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1037
    // 70     71     72     73     74     75     76     77
1038
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1039
    // 78     79     7A     7B     7C     7D     7E     7F
1040
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1041
    // 80     81     82     83     84     85     86     87
1042
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1043
    // 88     89     8A     8B     8C     8D     8E     8F
1044
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1045
    // 90     91     92     93     94     95     96     97
1046
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1047
    // 98     99     9A     9B     9C     9D     9E     9F
1048
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1049
    // A0     A1     A2     A3     A4     A5     A6     A7
1050
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1051
    // A8     A9     AA     AB     AC     AD     AE     AF
1052
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1053
    // B0     B1     B2     B3     B4     B5     B6     B7
1054
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1055
    // B8     B9     BA     BB     BC     BD     BE     BF
1056
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1057
    // C0     C1     C2     C3     C4     C5     C6     C7
1058
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1059
    // C8     C9     CA     CB     CC     CD     CE     CF
1060
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1061
    // D0     D1     D2     D3     D4     D5     D6     D7
1062
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1063
    // D8     D9     DA     DB     DC     DD     DE     DF
1064
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1065
    // E0     E1     E2     E3     E4     E5     E6     E7
1066
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1067
    // E8     E9     EA     EB     EC     ED     EE     EF
1068
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1069
    // F0     F1     F2     F3     F4     F5     F6     F7
1070
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1071
    // F8     F9     FA     FB     FC     FD     FE     FF
1072
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1073
1074
2.40M
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1075
2.40M
  return !!(a[i >> 3] & (1 << (i & 7)));
1076
2.40M
}
1077
1078
}  // namespace ada::character_sets
1079
1080
#endif  // ADA_CHARACTER_SETS_INL_H
1081
/* end file include/ada/character_sets-inl.h */
1082
/* begin file include/ada/checkers-inl.h */
1083
/**
1084
 * @file checkers-inl.h
1085
 * @brief Definitions for URL specific checkers used within Ada.
1086
 */
1087
#ifndef ADA_CHECKERS_INL_H
1088
#define ADA_CHECKERS_INL_H
1089
1090
#include <bit>
1091
#include <cstdint>
1092
#include <string_view>
1093
/* begin file include/ada/checkers.h */
1094
/**
1095
 * @file checkers.h
1096
 * @brief Declarations for URL specific checkers used within Ada.
1097
 */
1098
#ifndef ADA_CHECKERS_H
1099
#define ADA_CHECKERS_H
1100
1101
1102
#include <cstring>
1103
#include <string_view>
1104
1105
/**
1106
 * These functions are not part of our public API and may
1107
 * change at any time.
1108
 * @private
1109
 * @namespace ada::checkers
1110
 * @brief Includes the definitions for validation functions
1111
 */
1112
namespace ada::checkers {
1113
1114
/**
1115
 * @private
1116
 * Assuming that x is an ASCII letter, this function returns the lower case
1117
 * equivalent.
1118
 * @details More likely to be inlined by the compiler and constexpr.
1119
 */
1120
constexpr char to_lower(char x) noexcept;
1121
1122
/**
1123
 * @private
1124
 * Returns true if the character is an ASCII letter. Equivalent to std::isalpha
1125
 * but more likely to be inlined by the compiler.
1126
 *
1127
 * @attention std::isalpha is not constexpr generally.
1128
 */
1129
constexpr bool is_alpha(char x) noexcept;
1130
1131
/**
1132
 * @private
1133
 * Check whether x is an ASCII digit. More likely to be inlined than
1134
 * std::isdigit.
1135
 */
1136
constexpr bool is_digit(char x) noexcept;
1137
1138
/**
1139
 * @private
1140
 * @details A string starts with a Windows drive letter if all of the following
1141
 * are true:
1142
 *
1143
 *   - its length is greater than or equal to 2
1144
 *   - its first two code points are a Windows drive letter
1145
 *   - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F
1146
 * (?), or U+0023 (#).
1147
 *
1148
 * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
1149
 */
1150
inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept;
1151
1152
/**
1153
 * @private
1154
 * @details A normalized Windows drive letter is a Windows drive letter of which
1155
 * the second code point is U+003A (:).
1156
 */
1157
inline constexpr bool is_normalized_windows_drive_letter(
1158
    std::string_view input) noexcept;
1159
1160
/**
1161
 * @private
1162
 * Returns true if an input is an ipv4 address. It is assumed that the string
1163
 * does not contain uppercase ASCII characters (the input should have been
1164
 * lowered cased before calling this function) and is not empty.
1165
 */
1166
ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept;
1167
1168
/**
1169
 * @private
1170
 * Cheap pre-check for the WHATWG "ends in a number" / IPv4-host cases.
1171
 * False means the host cannot be IPv4: the last label is empty, does not
1172
 * start with an ASCII digit, or contains a character other than a hex
1173
 * digit or x/X. True means the slower IPv4 parsers should run.
1174
 *
1175
 * Safe on mixed-case input (unlike is_ipv4, which expects a lowercased
1176
 * host).
1177
 */
1178
ada_really_inline constexpr bool last_label_may_be_a_number(
1179
    std::string_view view) noexcept;
1180
1181
/**
1182
 * @private
1183
 * Returns a bitset. If the first bit is set, then at least one character needs
1184
 * percent encoding. If the second bit is set, a \\ is found. If the third bit
1185
 * is set then we have a dot. If the fourth bit is set, then we have a percent
1186
 * character.
1187
 */
1188
ada_really_inline constexpr uint8_t path_signature(
1189
    std::string_view input) noexcept;
1190
1191
/**
1192
 * @private
1193
 * Returns true if the length of the domain name and its labels are according to
1194
 * the specifications. The length of the domain must be 255 octets (253
1195
 * characters not including the last 2 which are the empty label reserved at the
1196
 * end). When the empty label is included (a dot at the end), the domain name
1197
 * can have 254 characters. The length of a label must be at least 1 and at most
1198
 * 63 characters.
1199
 * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034
1200
 * @see https://www.unicode.org/reports/tr46/#ToASCII
1201
 */
1202
ada_really_inline constexpr bool verify_dns_length(
1203
    std::string_view input) noexcept;
1204
1205
/**
1206
 * @private
1207
 * Fast-path parser for pure decimal IPv4 addresses (e.g., "192.168.1.1").
1208
 * Returns the packed 32-bit IPv4 address on success, or a value > 0xFFFFFFFF
1209
 * to indicate failure (caller should fall back to general parser).
1210
 * This is optimized for the common case where the input is a well-formed
1211
 * decimal IPv4 address with exactly 4 octets.
1212
 */
1213
ada_really_inline uint64_t try_parse_ipv4_fast(std::string_view input) noexcept;
1214
1215
/**
1216
 * Sentinel value indicating try_parse_ipv4_fast() did not succeed.
1217
 * Any value > 0xFFFFFFFF indicates the fast path should not be used.
1218
 */
1219
constexpr uint64_t ipv4_fast_fail = uint64_t(1) << 32;
1220
1221
}  // namespace ada::checkers
1222
1223
#endif  // ADA_CHECKERS_H
1224
/* end file include/ada/checkers.h */
1225
1226
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1227
#include <immintrin.h>
1228
#endif
1229
1230
namespace ada::checkers {
1231
1232
227k
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1233
1234
650k
constexpr bool is_ipv4_number_char(char x) noexcept {
1235
650k
  const unsigned char c = static_cast<unsigned char>(x);
1236
650k
  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
1237
490k
         (c >= 'A' && c <= 'F') || c == 'x' || c == 'X';
1238
650k
}
1239
1240
298k
constexpr bool last_label_may_be_a_number(std::string_view view) noexcept {
1241
298k
  if (view.empty()) {
1242
0
    return false;
1243
0
  }
1244
298k
  const char* start = view.data();
1245
298k
  const char* end = start + view.size();
1246
298k
  if (end[-1] == '.') {
1247
1.78k
    --end;
1248
1.78k
    if (end == start) {
1249
570
      return false;
1250
570
    }
1251
1.78k
  }
1252
298k
  if (!is_ipv4_number_char(end[-1])) {
1253
105k
    return false;
1254
105k
  }
1255
193k
  const char* label = end;
1256
523k
  while (label != start && is_ipv4_number_char(label[-1])) {
1257
330k
    --label;
1258
330k
  }
1259
193k
  if (label != start && label[-1] != '.') {
1260
4.34k
    return false;
1261
4.34k
  }
1262
188k
  return label != end && is_digit(*label);
1263
193k
}
1264
1265
668k
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1266
1267
342k
constexpr bool is_alpha(char x) noexcept {
1268
342k
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1269
342k
}
1270
1271
124k
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1272
124k
  return input.size() >= 2 &&
1273
40.3k
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1274
805
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1275
440
                                  input[2] == '?' || input[2] == '#'));
1276
124k
}
1277
1278
constexpr bool is_normalized_windows_drive_letter(
1279
1.89k
    std::string_view input) noexcept {
1280
1.89k
  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
1281
1.89k
}
1282
1283
namespace detail {
1284
1285
// Unrolled pure-decimal IPv4. The common portable path for 7-16 byte hosts.
1286
ada_really_inline uint64_t
1287
59.1k
parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
1288
59.1k
  uint32_t ipv4 = 0;
1289
240k
  for (int i = 0; i < 4; ++i) {
1290
195k
    if (p == pend) [[unlikely]] {
1291
85
      return ipv4_fast_fail;
1292
85
    }
1293
195k
    uint32_t val;
1294
195k
    char c = *p;
1295
195k
    if (c >= '0' && c <= '9') [[likely]] {
1296
184k
      val = static_cast<uint32_t>(c - '0');
1297
184k
      ++p;
1298
184k
    } else {
1299
10.5k
      return ipv4_fast_fail;
1300
10.5k
    }
1301
184k
    if (p < pend) {
1302
141k
      c = *p;
1303
141k
      if (c >= '0' && c <= '9') {
1304
50.4k
        if (val == 0) [[unlikely]] {
1305
463
          return ipv4_fast_fail;
1306
463
        }
1307
50.0k
        val = val * 10u + static_cast<uint32_t>(c - '0');
1308
50.0k
        ++p;
1309
50.0k
        if (p < pend) {
1310
49.7k
          c = *p;
1311
49.7k
          if (c >= '0' && c <= '9') {
1312
48.8k
            val = val * 10u + static_cast<uint32_t>(c - '0');
1313
48.8k
            ++p;
1314
48.8k
            if (val > 255u) [[unlikely]] {
1315
578
              return ipv4_fast_fail;
1316
578
            }
1317
48.8k
          }
1318
49.7k
        }
1319
50.0k
      }
1320
141k
    }
1321
183k
    ipv4 = (ipv4 << 8) | val;
1322
183k
    if (i < 3) {
1323
139k
      if (p == pend || *p != '.') [[unlikely]] {
1324
2.77k
        return ipv4_fast_fail;
1325
2.77k
      }
1326
136k
      ++p;
1327
136k
    }
1328
183k
  }
1329
44.6k
  if (p != pend) {
1330
725
    if (p == pend - 1 && *p == '.') {
1331
98
      return ipv4;
1332
98
    }
1333
627
    return ipv4_fast_fail;
1334
725
  }
1335
43.9k
  return ipv4;
1336
44.6k
}
1337
1338
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1339
// Table-free AVX-512VL IPv4 parse (simdip parse_ipv4_avx512vl_notab5).
1340
// Needs VBMI2 for vpcompressb; ADA_AVX512 stays BW+VL (IPv6 does not use
1341
// VBMI2). Masked load of exactly `len` bytes (no over-read). Digit
1342
// placement is computed from compressed delimiter positions; octet > 255
1343
// is a dword compare on the zero-padded reversed digit group, in parallel
1344
// with the convert. Unusual-but-valid forms (octal, hex, leading zeros,
1345
// fewer than four parts) return ipv4_fast_fail so the general parser runs.
1346
ada_really_inline uint64_t try_parse_ipv4_avx512(const char* data,
1347
                                                 size_t len) noexcept {
1348
  // One trailing dot is WHATWG-legal ("1.2.3.4."); the SIMD kernel is
1349
  // strict four-group dotted-decimal.
1350
  if (data[len - 1] == '.') {
1351
    --len;
1352
  }
1353
  if (len > 15) [[unlikely]] {
1354
    return ipv4_fast_fail;
1355
  }
1356
1357
#if defined(__BMI2__)
1358
  const uint32_t len_mask = _bzhi_u32(0xFFFFFFFFu, static_cast<unsigned>(len));
1359
#else
1360
  const uint32_t len_mask = (1u << static_cast<unsigned>(len)) - 1u;
1361
#endif
1362
  const __mmask16 len_k = static_cast<__mmask16>(len_mask);
1363
  const __m128i dot = _mm_set1_epi8('.');
1364
  const __m128i v =
1365
      _mm_mask_loadu_epi8(dot, len_k, reinterpret_cast<const void*>(data));
1366
1367
  const __mmask16 delim = _mm_cmpeq_epi8_mask(v, dot);
1368
  const uint32_t dots = static_cast<uint32_t>(delim) & len_mask;
1369
  const uint32_t keep = len_mask & ~dots;
1370
1371
  const __m128i zero = _mm_set1_epi8('0');
1372
  const __m128i digits = _mm_sub_epi8(v, zero);
1373
  const __mmask16 is_digit = _mm_cmple_epu8_mask(digits, _mm_set1_epi8(9));
1374
  // Junk in a digit slot: inside [0, len) yet neither a digit nor a dot.
1375
  const __mmask16 hole = _kandn_mask16(_kor_mask16(delim, is_digit), len_k);
1376
  const __m128i v0 = _mm_maskz_mov_epi8(is_digit, digits);
1377
1378
  const __m128i iota =
1379
      _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
1380
  const __m128i c = _mm_maskz_compress_epi8(delim, iota);
1381
  const __m128i k_rep =
1382
      _mm_setr_epi8(0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3);
1383
  const __m128i qi = _mm_shuffle_epi8(c, k_rep);
1384
  const __m128i prev =
1385
      _mm_shuffle_epi8(_mm_alignr_epi8(c, _mm_set1_epi8(-1), 15), k_rep);
1386
  // Reversed group order: lane 4i+j fetches q_i-(j+1), so the dword is
1387
  // ones | tens<<8 | hundreds<<16, monotone in the decimal value.
1388
  const __m128i offr = _mm_setr_epi8(-1, -2, -3, -4, -1, -2, -3, -4, -1, -2, -3,
1389
                                     -4, -1, -2, -3, -4);
1390
  const __m128i idx = _mm_max_epi8(_mm_add_epi8(qi, offr), prev);
1391
  const __m128i padded = _mm_shuffle_epi8(v0, idx);
1392
1393
  const __m128i lim =
1394
      _mm_setr_epi8(5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0);
1395
  const __mmask8 over = _mm_cmpgt_epu32_mask(padded, lim);
1396
  const __m128i wtsr =
1397
      _mm_setr_epi8(1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0);
1398
#if defined(__AVX512VNNI__)
1399
  const __m128i res = _mm_dpbusd_epi32(_mm_setzero_si128(), padded, wtsr);
1400
#else
1401
  const __m128i res =
1402
      _mm_madd_epi16(_mm_maddubs_epi16(padded, wtsr), _mm_set1_epi16(1));
1403
#endif
1404
1405
  const __m128i gmin =
1406
      _mm_setr_epi8(1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1407
  const __m128i gmax =
1408
      _mm_setr_epi8(2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
1409
  const __m128i gap = _mm_sub_epi8(c, _mm_slli_si128(c, 1));
1410
  const __mmask16 bad_gap = _mm_cmpgt_epu8_mask(_mm_sub_epi8(gap, gmin), gmax);
1411
1412
  const uint32_t zero_bits =
1413
      static_cast<uint32_t>(_mm_cmpeq_epi8_mask(v, zero));
1414
  const uint32_t start_bits = (dots << 1) | 1u;
1415
1416
  const __mmask16 kerr =
1417
      _kor_mask16(_kor_mask16(hole, static_cast<__mmask16>(over)), bad_gap);
1418
  const uint32_t gerr = static_cast<uint32_t>(_mm_popcnt_u32(dots) ^ 3u) |
1419
                        (zero_bits & start_bits & (keep >> 1));
1420
1421
  if ((gerr | static_cast<uint32_t>(kerr)) == 0) [[likely]] {
1422
    const uint32_t packed =
1423
        static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_cvtepi32_epi8(res)));
1424
#if defined(__GNUC__) || defined(__clang__)
1425
    return static_cast<uint64_t>(__builtin_bswap32(packed));
1426
#else
1427
    return static_cast<uint64_t>(_byteswap_ulong(packed));
1428
#endif
1429
  }
1430
  return ipv4_fast_fail;
1431
}
1432
#endif  // ADA_AVX512 && __AVX512VBMI2__
1433
1434
}  // namespace detail
1435
1436
/**
1437
 * Fast pure-decimal IPv4 parse. Returns packed address or ipv4_fast_fail.
1438
 * Accepts an optional single trailing dot.
1439
 *
1440
 * On AVX-512BW+VL+VBMI2 targets, uses a table-free SIMD parse (masked
1441
 * load, no source over-read) based on parse_ipv4_avx512vl_notab5.
1442
 * Otherwise uses an unrolled scalar path (typically faster than SSE2/NEON
1443
 * pre-validation for these 7-16 byte hosts).
1444
 */
1445
ada_really_inline uint64_t
1446
288k
try_parse_ipv4_fast(std::string_view input) noexcept {
1447
288k
  const size_t len = input.size();
1448
  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
1449
288k
  if (len < 7 || len > 16) [[unlikely]] {
1450
229k
    return ipv4_fast_fail;
1451
229k
  }
1452
59.1k
  const char* data = input.data();
1453
1454
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1455
  return detail::try_parse_ipv4_avx512(data, len);
1456
#else
1457
59.1k
  return detail::parse_ipv4_decimal_scalar(data, data + len);
1458
288k
#endif
1459
288k
}
1460
1461
}  // namespace ada::checkers
1462
1463
#endif  // ADA_CHECKERS_INL_H
1464
/* end file include/ada/checkers-inl.h */
1465
/* begin file include/ada/log.h */
1466
/**
1467
 * @file log.h
1468
 * @brief Includes the definitions for logging.
1469
 * @private Excluded from docs through the doxygen file.
1470
 */
1471
#ifndef ADA_LOG_H
1472
#define ADA_LOG_H
1473
1474
// To enable logging, set ADA_LOGGING to 1:
1475
#ifndef ADA_LOGGING
1476
#define ADA_LOGGING 0
1477
#endif
1478
1479
#if ADA_LOGGING
1480
#include <iostream>
1481
#endif  // ADA_LOGGING
1482
1483
namespace ada {
1484
1485
/**
1486
 * Log a message. If you want to have no overhead when logging is disabled, use
1487
 * the ada_log macro.
1488
 * @private
1489
 */
1490
template <typename... Args>
1491
constexpr ada_really_inline void log([[maybe_unused]] Args... args) {
1492
#if ADA_LOGGING
1493
  ((std::cout << "ADA_LOG: ") << ... << args) << std::endl;
1494
#endif  // ADA_LOGGING
1495
}
1496
}  // namespace ada
1497
1498
#if ADA_LOGGING
1499
#ifndef ada_log
1500
#define ada_log(...)       \
1501
  do {                     \
1502
    ada::log(__VA_ARGS__); \
1503
  } while (0)
1504
#endif  // ada_log
1505
#else
1506
#define ada_log(...)
1507
#endif  // ADA_LOGGING
1508
1509
#endif  // ADA_LOG_H
1510
/* end file include/ada/log.h */
1511
/* begin file include/ada/encoding_type.h */
1512
/**
1513
 * @file encoding_type.h
1514
 * @brief Character encoding type definitions.
1515
 *
1516
 * Defines the encoding types supported for URL processing.
1517
 *
1518
 * @see https://encoding.spec.whatwg.org/
1519
 */
1520
#ifndef ADA_ENCODING_TYPE_H
1521
#define ADA_ENCODING_TYPE_H
1522
1523
#include <string>
1524
1525
namespace ada {
1526
1527
/**
1528
 * @brief Character encoding types for URL processing.
1529
 *
1530
 * Specifies the character encoding used for percent-decoding and other
1531
 * string operations. UTF-8 is the most commonly used encoding for URLs.
1532
 *
1533
 * @see https://encoding.spec.whatwg.org/#encodings
1534
 */
1535
enum class encoding_type {
1536
  UTF8,     /**< UTF-8 encoding (default for URLs) */
1537
  UTF_16LE, /**< UTF-16 Little Endian encoding */
1538
  UTF_16BE, /**< UTF-16 Big Endian encoding */
1539
};
1540
1541
/**
1542
 * Converts an encoding_type to its string representation.
1543
 * @param type The encoding type to convert.
1544
 * @return A string view of the encoding name.
1545
 */
1546
ada_warn_unused std::string_view to_string(encoding_type type);
1547
1548
}  // namespace ada
1549
1550
#endif  // ADA_ENCODING_TYPE_H
1551
/* end file include/ada/encoding_type.h */
1552
/* begin file include/ada/helpers.h */
1553
/**
1554
 * @file helpers.h
1555
 * @brief Definitions for helper functions used within Ada.
1556
 */
1557
#ifndef ADA_HELPERS_H
1558
#define ADA_HELPERS_H
1559
1560
/* begin file include/ada/url_base.h */
1561
/**
1562
 * @file url_base.h
1563
 * @brief Base class and common definitions for URL types.
1564
 *
1565
 * This file defines the `url_base` abstract base class from which both
1566
 * `ada::url` and `ada::url_aggregator` inherit. It also defines common
1567
 * enumerations like `url_host_type`.
1568
 */
1569
#ifndef ADA_URL_BASE_H
1570
#define ADA_URL_BASE_H
1571
1572
/* begin file include/ada/scheme.h */
1573
/**
1574
 * @file scheme.h
1575
 * @brief URL scheme type definitions and utilities.
1576
 *
1577
 * This header defines the URL scheme types (http, https, etc.) and provides
1578
 * functions to identify special schemes and their default ports according
1579
 * to the WHATWG URL Standard.
1580
 *
1581
 * @see https://url.spec.whatwg.org/#special-scheme
1582
 */
1583
#ifndef ADA_SCHEME_H
1584
#define ADA_SCHEME_H
1585
1586
1587
#include <string>
1588
1589
/**
1590
 * @namespace ada::scheme
1591
 * @brief URL scheme utilities and constants.
1592
 *
1593
 * Provides functions for working with URL schemes, including identification
1594
 * of special schemes and retrieval of default port numbers.
1595
 */
1596
namespace ada::scheme {
1597
1598
/**
1599
 * @brief Enumeration of URL scheme types.
1600
 *
1601
 * Special schemes have specific parsing rules and default ports.
1602
 * Using an enum allows efficient scheme comparisons without string operations.
1603
 *
1604
 * Default ports:
1605
 * - HTTP: 80
1606
 * - HTTPS: 443
1607
 * - WS: 80
1608
 * - WSS: 443
1609
 * - FTP: 21
1610
 * - FILE: (none)
1611
 */
1612
enum type : uint8_t {
1613
  HTTP = 0,        /**< http:// scheme (port 80) */
1614
  NOT_SPECIAL = 1, /**< Non-special scheme (no default port) */
1615
  HTTPS = 2,       /**< https:// scheme (port 443) */
1616
  WS = 3,          /**< ws:// WebSocket scheme (port 80) */
1617
  FTP = 4,         /**< ftp:// scheme (port 21) */
1618
  WSS = 5,         /**< wss:// secure WebSocket scheme (port 443) */
1619
  FILE = 6         /**< file:// scheme (no default port) */
1620
};
1621
1622
/**
1623
 * Checks if a scheme string is a special scheme.
1624
 * @param scheme The scheme string to check (e.g., "http", "https").
1625
 * @return `true` if the scheme is special, `false` otherwise.
1626
 * @see https://url.spec.whatwg.org/#special-scheme
1627
 */
1628
ada_really_inline constexpr bool is_special(std::string_view scheme);
1629
1630
/**
1631
 * Returns the default port for a special scheme string.
1632
 * @param scheme The scheme string (e.g., "http", "https").
1633
 * @return The default port number, or 0 if not a special scheme.
1634
 * @see https://url.spec.whatwg.org/#special-scheme
1635
 */
1636
constexpr uint16_t get_special_port(std::string_view scheme) noexcept;
1637
1638
/**
1639
 * Returns the default port for a scheme type.
1640
 * @param type The scheme type enum value.
1641
 * @return The default port number, or 0 if not applicable.
1642
 * @see https://url.spec.whatwg.org/#special-scheme
1643
 */
1644
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept;
1645
1646
/**
1647
 * Converts a scheme string to its type enum.
1648
 * @param scheme The scheme string to convert.
1649
 * @return The corresponding scheme type, or NOT_SPECIAL if not recognized.
1650
 */
1651
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept;
1652
1653
}  // namespace ada::scheme
1654
1655
#endif  // ADA_SCHEME_H
1656
/* end file include/ada/scheme.h */
1657
1658
#include <string>
1659
#include <string_view>
1660
1661
namespace ada {
1662
1663
/**
1664
 * @brief Enum representing the type of host in a URL.
1665
 *
1666
 * Used to distinguish between regular domain names, IPv4 addresses,
1667
 * and IPv6 addresses for proper parsing and serialization.
1668
 */
1669
enum url_host_type : uint8_t {
1670
  /** Regular domain name (e.g., "www.example.com") */
1671
  DEFAULT = 0,
1672
  /** IPv4 address (e.g., "127.0.0.1") */
1673
  IPV4 = 1,
1674
  /** IPv6 address (e.g., "[::1]" or "[2001:db8::1]") */
1675
  IPV6 = 2,
1676
};
1677
1678
/**
1679
 * @brief Abstract base class for URL representations.
1680
 *
1681
 * The `url_base` class provides the common interface and state shared by
1682
 * both `ada::url` and `ada::url_aggregator`. It contains basic URL attributes
1683
 * like validity status and scheme type, but delegates component storage and
1684
 * access to derived classes.
1685
 *
1686
 * @note This is an abstract class and cannot be instantiated directly.
1687
 *       Use `ada::url` or `ada::url_aggregator` instead.
1688
 *
1689
 * @see url
1690
 * @see url_aggregator
1691
 */
1692
struct url_base {
1693
695k
  virtual ~url_base() = default;
1694
1695
  /**
1696
   * Indicates whether the URL was successfully parsed.
1697
   * Set to `false` if parsing failed (e.g., invalid URL syntax).
1698
   */
1699
  bool is_valid{true};
1700
1701
  /**
1702
   * Indicates whether the URL has an opaque path (non-hierarchical).
1703
   * Opaque paths occur in non-special URLs like `mailto:` or `javascript:`.
1704
   */
1705
  bool has_opaque_path{false};
1706
1707
  /**
1708
   * The type of the URL's host (domain, IPv4, or IPv6).
1709
   */
1710
  url_host_type host_type = url_host_type::DEFAULT;
1711
1712
  /**
1713
   * @private
1714
   * Internal representation of the URL's scheme type.
1715
   */
1716
  ada::scheme::type type{ada::scheme::type::NOT_SPECIAL};
1717
1718
  /**
1719
   * Checks if the URL has a special scheme (http, https, ws, wss, ftp, file).
1720
   * Special schemes have specific parsing rules and default ports.
1721
   * @return `true` if the scheme is special, `false` otherwise.
1722
   */
1723
  [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept;
1724
1725
  /**
1726
   * Returns the URL's origin (scheme + host + port for special URLs).
1727
   * @return A newly allocated string containing the serialized origin.
1728
   * @see https://url.spec.whatwg.org/#concept-url-origin
1729
   */
1730
  [[nodiscard]] virtual std::string get_origin() const = 0;
1731
1732
  /**
1733
   * Validates whether the hostname is a valid domain according to RFC 1034.
1734
   * Checks that the domain and its labels have valid lengths.
1735
   * @return `true` if the domain is valid, `false` otherwise.
1736
   */
1737
  [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0;
1738
1739
  /**
1740
   * @private
1741
   * Returns the default port for special schemes (e.g., 443 for https).
1742
   * Returns 0 for file:// URLs or non-special schemes.
1743
   */
1744
  [[nodiscard]] inline uint16_t get_special_port() const noexcept;
1745
1746
  /**
1747
   * @private
1748
   * Returns the default port for the URL's scheme, or 0 if none.
1749
   */
1750
  [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept;
1751
1752
  /**
1753
   * @private
1754
   * Parses a port number from the input string.
1755
   * @param view The string containing the port to parse.
1756
   * @param check_trailing_content Whether to validate no trailing characters.
1757
   * @return Number of bytes consumed on success, 0 on failure.
1758
   */
1759
  virtual size_t parse_port(std::string_view view,
1760
                            bool check_trailing_content) = 0;
1761
1762
  /** @private */
1763
0
  virtual ada_really_inline size_t parse_port(std::string_view view) {
1764
0
    return this->parse_port(view, false);
1765
0
  }
1766
1767
  /**
1768
   * Returns a JSON string representation of this URL for debugging.
1769
   * @return A JSON-formatted string with URL information.
1770
   */
1771
  [[nodiscard]] virtual std::string to_string() const = 0;
1772
1773
  /** @private */
1774
  virtual inline void clear_pathname() = 0;
1775
1776
  /** @private */
1777
  virtual inline void clear_search() = 0;
1778
1779
  /** @private */
1780
  [[nodiscard]] virtual inline bool has_hash() const noexcept = 0;
1781
1782
  /** @private */
1783
  [[nodiscard]] virtual inline bool has_search() const noexcept = 0;
1784
1785
};  // url_base
1786
1787
}  // namespace ada
1788
1789
#endif
1790
/* end file include/ada/url_base.h */
1791
1792
#include <string>
1793
#include <string_view>
1794
#include <optional>
1795
1796
#if ADA_DEVELOPMENT_CHECKS
1797
#include <iostream>
1798
#endif  // ADA_DEVELOPMENT_CHECKS
1799
1800
/**
1801
 * These functions are not part of our public API and may
1802
 * change at any time.
1803
 *
1804
 * @private
1805
 * @namespace ada::helpers
1806
 * @brief Includes the definitions for helper functions
1807
 */
1808
namespace ada::helpers {
1809
1810
/**
1811
 * @private
1812
 */
1813
template <typename out_iter>
1814
void encode_json(std::string_view view, out_iter out);
1815
1816
/**
1817
 * @private
1818
 * This function is used to prune a fragment from a url, and returning the
1819
 * removed string if input has fragment.
1820
 *
1821
 * @details prune_hash seeks the first '#' and returns everything after it
1822
 * as a string_view, and modifies (in place) the input so that it points at
1823
 * everything before the '#'. If no '#' is found, the input is left unchanged
1824
 * and std::nullopt is returned.
1825
 *
1826
 * @attention The function is non-allocating and it does not throw.
1827
 * @returns Note that the returned string_view might be empty!
1828
 */
1829
ada_really_inline std::optional<std::string_view> prune_hash(
1830
    std::string_view& input) noexcept;
1831
1832
/**
1833
 * @private
1834
 * Defined by the URL specification, shorten a URLs paths.
1835
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1836
 * @returns Returns true if path is shortened.
1837
 */
1838
ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type);
1839
1840
/**
1841
 * @private
1842
 * Defined by the URL specification, shorten a URLs paths.
1843
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1844
 * @returns Returns true if path is shortened.
1845
 */
1846
ada_really_inline bool shorten_path(std::string_view& path,
1847
                                    ada::scheme::type type);
1848
1849
/**
1850
 * @private
1851
 *
1852
 * Parse the path from the provided input and append to the existing
1853
 * (possibly empty) path. The input cannot contain tabs and spaces: it
1854
 * is the user's responsibility to check.
1855
 *
1856
 * The input is expected to be UTF-8.
1857
 *
1858
 * @see https://url.spec.whatwg.org/
1859
 */
1860
ada_really_inline void parse_prepared_path(std::string_view input,
1861
                                           ada::scheme::type type,
1862
                                           std::string& path);
1863
1864
/**
1865
 * @private
1866
 * Remove and mutate all ASCII tab or newline characters from an input.
1867
 */
1868
ada_really_inline void remove_ascii_tab_or_newline(std::string& input);
1869
1870
/**
1871
 * @private
1872
 * Return the substring from input going from index pos to the end.
1873
 */
1874
ada_really_inline constexpr std::string_view substring(std::string_view input,
1875
                                                       size_t pos);
1876
1877
/**
1878
 * @private
1879
 * Returns true if the string_view points within the string.
1880
 */
1881
bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1882
1883
/**
1884
 * @private
1885
 * Return the substring from input going from index pos1 to the pos2 (non
1886
 * included). The length of the substring is pos2 - pos1.
1887
 */
1888
ada_really_inline constexpr std::string_view substring(std::string_view input,
1889
                                                       size_t pos1,
1890
677k
                                                       size_t pos2) {
1891
#if ADA_DEVELOPMENT_CHECKS
1892
  if (pos2 < pos1) {
1893
    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1894
              << std::endl;
1895
    abort();
1896
  }
1897
#endif
1898
677k
  return input.substr(pos1, pos2 - pos1);
1899
677k
}
1900
1901
/**
1902
 * @private
1903
 * Modify the string_view so that it has the new size pos, assuming that pos <=
1904
 * input.size(). This function cannot throw.
1905
 */
1906
ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1907
1908
/**
1909
 * @private
1910
 * Returns a host's delimiter location depending on the state of the instance,
1911
 * and whether a colon was found outside brackets. Used by the host parser.
1912
 */
1913
ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1914
    bool is_special, std::string_view& view) noexcept;
1915
1916
/**
1917
 * @private
1918
 * Removes leading and trailing C0 control and whitespace characters from
1919
 * string.
1920
 */
1921
void trim_c0_whitespace(std::string_view& input) noexcept;
1922
1923
/**
1924
 * @private
1925
 * @see
1926
 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1927
 */
1928
template <class url_type>
1929
ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url);
1930
1931
/**
1932
 * @private
1933
 * Finds the delimiter of a view in authority state.
1934
 */
1935
ada_really_inline size_t
1936
find_authority_delimiter_special(std::string_view view) noexcept;
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(std::string_view view) noexcept;
1944
1945
/**
1946
 * @private
1947
 */
1948
template <typename T, typename... Args>
1949
104k
inline void inner_concat(std::string& buffer, T t) {
1950
104k
  buffer.append(t);
1951
104k
}
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
1949
42.4k
inline void inner_concat(std::string& buffer, T t) {
1950
42.4k
  buffer.append(t);
1951
42.4k
}
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
1949
13.2k
inline void inner_concat(std::string& buffer, T t) {
1950
13.2k
  buffer.append(t);
1951
13.2k
}
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
1949
48.6k
inline void inner_concat(std::string& buffer, T t) {
1950
48.6k
  buffer.append(t);
1951
48.6k
}
1952
1953
/**
1954
 * @private
1955
 */
1956
template <typename T, typename... Args>
1957
153k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1958
153k
  buffer.append(t);
1959
153k
  return inner_concat(buffer, args...);
1960
153k
}
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
1957
42.4k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1958
42.4k
  buffer.append(t);
1959
42.4k
  return inner_concat(buffer, args...);
1960
42.4k
}
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Line
Count
Source
1957
13.2k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1958
13.2k
  buffer.append(t);
1959
13.2k
  return inner_concat(buffer, args...);
1960
13.2k
}
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
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
1957
48.6k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1958
48.6k
  buffer.append(t);
1959
48.6k
  return inner_concat(buffer, args...);
1960
48.6k
}
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
1957
48.6k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1958
48.6k
  buffer.append(t);
1959
48.6k
  return inner_concat(buffer, args...);
1960
48.6k
}
1961
1962
/**
1963
 * @private
1964
 * Concatenate the arguments and return a string.
1965
 * @returns a string
1966
 */
1967
template <typename... Args>
1968
104k
std::string concat(Args... args) {
1969
104k
  std::string answer;
1970
104k
  inner_concat(answer, args...);
1971
104k
  return answer;
1972
104k
}
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
1968
42.4k
std::string concat(Args... args) {
1969
42.4k
  std::string answer;
1970
42.4k
  inner_concat(answer, args...);
1971
42.4k
  return answer;
1972
42.4k
}
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Line
Count
Source
1968
13.2k
std::string concat(Args... args) {
1969
13.2k
  std::string answer;
1970
13.2k
  inner_concat(answer, args...);
1971
13.2k
  return answer;
1972
13.2k
}
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
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
1968
2
std::string concat(Args... args) {
1969
2
  std::string answer;
1970
2
  inner_concat(answer, args...);
1971
2
  return answer;
1972
2
}
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
1968
48.6k
std::string concat(Args... args) {
1969
48.6k
  std::string answer;
1970
48.6k
  inner_concat(answer, args...);
1971
48.6k
  return answer;
1972
48.6k
}
1973
1974
/**
1975
 * @private
1976
 * @return Number of leading zeroes.
1977
 */
1978
176k
inline int leading_zeroes(uint32_t input_num) noexcept {
1979
#if ADA_REGULAR_VISUAL_STUDIO
1980
  unsigned long leading_zero(0);
1981
  unsigned long in(input_num);
1982
  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
1983
#else
1984
176k
  return __builtin_clz(input_num);
1985
176k
#endif  // ADA_REGULAR_VISUAL_STUDIO
1986
176k
}
1987
1988
/**
1989
 * @private
1990
 * Counts the number of decimal digits necessary to represent x.
1991
 * faster than std::to_string(x).size().
1992
 * @return digit count
1993
 */
1994
0
inline int fast_digit_count(uint32_t x) noexcept {
1995
0
  auto int_log2 = [](uint32_t z) -> int {
1996
0
    return 31 - ada::helpers::leading_zeroes(z | 1);
1997
0
  };
1998
0
  // Compiles to very few instructions. Note that the
1999
0
  // table is static and thus effectively a constant.
2000
0
  // We leave it inside the function because it is meaningless
2001
0
  // outside of it (this comes at no performance cost).
2002
0
  const static uint64_t table[] = {
2003
0
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
2004
0
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
2005
0
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
2006
0
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
2007
0
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
2008
0
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
2009
0
      42949672960, 42949672960};
2010
0
  return int((x + table[int_log2(x)]) >> 32);
2011
0
}
2012
}  // namespace ada::helpers
2013
2014
#endif  // ADA_HELPERS_H
2015
/* end file include/ada/helpers.h */
2016
/* begin file include/ada/parser.h */
2017
/**
2018
 * @file parser.h
2019
 * @brief Low-level URL parsing functions.
2020
 *
2021
 * This header provides the internal URL parsing implementation. Most users
2022
 * should use `ada::parse()` from implementation.h instead of these functions
2023
 * directly.
2024
 *
2025
 * @see implementation.h for the recommended public API
2026
 */
2027
#ifndef ADA_PARSER_H
2028
#define ADA_PARSER_H
2029
2030
#include <string_view>
2031
#include <variant>
2032
2033
/* begin file include/ada/expected.h */
2034
/**
2035
 * @file expected.h
2036
 * @brief Definitions for std::expected
2037
 * @private Excluded from docs through the doxygen file.
2038
 */
2039
///
2040
// expected - An implementation of std::expected with extensions
2041
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
2042
//
2043
// Documentation available at http://tl.tartanllama.xyz/
2044
//
2045
// To the extent possible under law, the author(s) have dedicated all
2046
// copyright and related and neighboring rights to this software to the
2047
// public domain worldwide. This software is distributed without any warranty.
2048
//
2049
// You should have received a copy of the CC0 Public Domain Dedication
2050
// along with this software. If not, see
2051
// <http://creativecommons.org/publicdomain/zero/1.0/>.
2052
///
2053
2054
#ifndef TL_EXPECTED_HPP
2055
#define TL_EXPECTED_HPP
2056
2057
#define TL_EXPECTED_VERSION_MAJOR 1
2058
#define TL_EXPECTED_VERSION_MINOR 1
2059
#define TL_EXPECTED_VERSION_PATCH 0
2060
2061
#include <exception>
2062
#include <functional>
2063
#include <type_traits>
2064
#include <utility>
2065
2066
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
2067
#define TL_EXPECTED_EXCEPTIONS_ENABLED
2068
#endif
2069
2070
#if (defined(_MSC_VER) && _MSC_VER == 1900)
2071
#define TL_EXPECTED_MSVC2015
2072
#define TL_EXPECTED_MSVC2015_CONSTEXPR
2073
#else
2074
#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
2075
#endif
2076
2077
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2078
     !defined(__clang__))
2079
#define TL_EXPECTED_GCC49
2080
#endif
2081
2082
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
2083
     !defined(__clang__))
2084
#define TL_EXPECTED_GCC54
2085
#endif
2086
2087
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
2088
     !defined(__clang__))
2089
#define TL_EXPECTED_GCC55
2090
#endif
2091
2092
#if !defined(TL_ASSERT)
2093
// can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
2094
#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
2095
#include <cassert>
2096
3.02M
#define TL_ASSERT(x) assert(x)
2097
#else
2098
#define TL_ASSERT(x)
2099
#endif
2100
#endif
2101
2102
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2103
     !defined(__clang__))
2104
// GCC < 5 doesn't support overloading on const&& for member functions
2105
2106
#define TL_EXPECTED_NO_CONSTRR
2107
// GCC < 5 doesn't support some standard C++11 type traits
2108
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2109
  std::has_trivial_copy_constructor<T>
2110
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2111
  std::has_trivial_copy_assign<T>
2112
2113
// This one will be different for GCC 5.7 if it's ever supported
2114
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2115
  std::is_trivially_destructible<T>
2116
2117
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
2118
// std::vector for non-copyable types
2119
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
2120
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2121
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2122
namespace tl {
2123
namespace detail {
2124
template <class T>
2125
struct is_trivially_copy_constructible
2126
    : std::is_trivially_copy_constructible<T> {};
2127
#ifdef _GLIBCXX_VECTOR
2128
template <class T, class A>
2129
struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
2130
#endif
2131
}  // namespace detail
2132
}  // namespace tl
2133
#endif
2134
2135
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2136
  tl::detail::is_trivially_copy_constructible<T>
2137
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2138
  std::is_trivially_copy_assignable<T>
2139
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2140
  std::is_trivially_destructible<T>
2141
#else
2142
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2143
  std::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
#endif
2149
2150
#if __cplusplus > 201103L
2151
#define TL_EXPECTED_CXX14
2152
#endif
2153
2154
#ifdef TL_EXPECTED_GCC49
2155
#define TL_EXPECTED_GCC49_CONSTEXPR
2156
#else
2157
#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
2158
#endif
2159
2160
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
2161
     defined(TL_EXPECTED_GCC49))
2162
#define TL_EXPECTED_11_CONSTEXPR
2163
#else
2164
#define TL_EXPECTED_11_CONSTEXPR constexpr
2165
#endif
2166
2167
namespace tl {
2168
template <class T, class E>
2169
class expected;
2170
2171
#ifndef TL_MONOSTATE_INPLACE_MUTEX
2172
#define TL_MONOSTATE_INPLACE_MUTEX
2173
class monostate {};
2174
2175
struct in_place_t {
2176
  explicit in_place_t() = default;
2177
};
2178
static constexpr in_place_t in_place{};
2179
#endif
2180
2181
template <class E>
2182
class unexpected {
2183
 public:
2184
  static_assert(!std::is_same<E, void>::value, "E must not be void");
2185
2186
  unexpected() = delete;
2187
  constexpr explicit unexpected(const E& e) : m_val(e) {}
2188
2189
30.6k
  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
2190
2191
  template <class... Args, typename std::enable_if<std::is_constructible<
2192
                               E, Args&&...>::value>::type* = nullptr>
2193
  constexpr explicit unexpected(Args&&... args)
2194
0
      : m_val(std::forward<Args>(args)...) {}
2195
  template <
2196
      class U, class... Args,
2197
      typename std::enable_if<std::is_constructible<
2198
          E, std::initializer_list<U>&, Args&&...>::value>::type* = nullptr>
2199
  constexpr explicit unexpected(std::initializer_list<U> l, Args&&... args)
2200
      : m_val(l, std::forward<Args>(args)...) {}
2201
2202
  constexpr const E& value() const& { return m_val; }
2203
15.3k
  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
2204
  TL_EXPECTED_11_CONSTEXPR E&& value() && { return std::move(m_val); }
2205
  constexpr const E&& value() const&& { return std::move(m_val); }
2206
2207
 private:
2208
  E m_val;
2209
};
2210
2211
#ifdef __cpp_deduction_guides
2212
template <class E>
2213
unexpected(E) -> unexpected<E>;
2214
#endif
2215
2216
template <class E>
2217
constexpr bool operator==(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2218
  return lhs.value() == rhs.value();
2219
}
2220
template <class E>
2221
constexpr bool operator!=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2222
  return lhs.value() != rhs.value();
2223
}
2224
template <class E>
2225
constexpr bool operator<(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2226
  return lhs.value() < rhs.value();
2227
}
2228
template <class E>
2229
constexpr bool operator<=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2230
  return lhs.value() <= rhs.value();
2231
}
2232
template <class E>
2233
constexpr bool operator>(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2234
  return lhs.value() > rhs.value();
2235
}
2236
template <class E>
2237
constexpr bool operator>=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2238
  return lhs.value() >= rhs.value();
2239
}
2240
2241
template <class E>
2242
unexpected<typename std::decay<E>::type> make_unexpected(E&& e) {
2243
  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
2244
}
2245
2246
struct unexpect_t {
2247
  unexpect_t() = default;
2248
};
2249
static constexpr unexpect_t unexpect{};
2250
2251
namespace detail {
2252
template <typename E>
2253
0
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E&& e) {
2254
0
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2255
0
  throw std::forward<E>(e);
2256
#else
2257
  (void)e;
2258
#ifdef _MSC_VER
2259
  __assume(0);
2260
#else
2261
  __builtin_unreachable();
2262
#endif
2263
#endif
2264
0
}
2265
2266
#ifndef TL_TRAITS_MUTEX
2267
#define TL_TRAITS_MUTEX
2268
// C++14-style aliases for brevity
2269
template <class T>
2270
using remove_const_t = typename std::remove_const<T>::type;
2271
template <class T>
2272
using remove_reference_t = typename std::remove_reference<T>::type;
2273
template <class T>
2274
using decay_t = typename std::decay<T>::type;
2275
template <bool E, class T = void>
2276
using enable_if_t = typename std::enable_if<E, T>::type;
2277
template <bool B, class T, class F>
2278
using conditional_t = typename std::conditional<B, T, F>::type;
2279
2280
// std::conjunction from C++17
2281
template <class...>
2282
struct conjunction : std::true_type {};
2283
template <class B>
2284
struct conjunction<B> : B {};
2285
template <class B, class... Bs>
2286
struct conjunction<B, Bs...>
2287
    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2288
2289
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2290
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2291
#endif
2292
2293
// In C++11 mode, there's an issue in libc++'s std::mem_fn
2294
// which results in a hard-error when using it in a noexcept expression
2295
// in some cases. This is a check to workaround the common failing case.
2296
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2297
template <class T>
2298
struct is_pointer_to_non_const_member_func : std::false_type {};
2299
template <class T, class Ret, class... Args>
2300
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
2301
    : std::true_type {};
2302
template <class T, class Ret, class... Args>
2303
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
2304
    : std::true_type {};
2305
template <class T, class Ret, class... Args>
2306
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
2307
    : std::true_type {};
2308
template <class T, class Ret, class... Args>
2309
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
2310
    : std::true_type {};
2311
template <class T, class Ret, class... Args>
2312
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&>
2313
    : std::true_type {};
2314
template <class T, class Ret, class... Args>
2315
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&>
2316
    : std::true_type {};
2317
2318
template <class T>
2319
struct is_const_or_const_ref : std::false_type {};
2320
template <class T>
2321
struct is_const_or_const_ref<T const&> : std::true_type {};
2322
template <class T>
2323
struct is_const_or_const_ref<T const> : std::true_type {};
2324
#endif
2325
2326
// std::invoke from C++17
2327
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2328
template <
2329
    typename Fn, typename... Args,
2330
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2331
    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2332
                             is_const_or_const_ref<Args...>::value)>,
2333
#endif
2334
    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2335
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2336
    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2337
    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2338
  return std::mem_fn(f)(std::forward<Args>(args)...);
2339
}
2340
2341
template <typename Fn, typename... Args,
2342
          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2343
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2344
    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2345
    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2346
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
2347
}
2348
2349
// std::invoke_result from C++17
2350
template <class F, class, class... Us>
2351
struct invoke_result_impl;
2352
2353
template <class F, class... Us>
2354
struct invoke_result_impl<
2355
    F,
2356
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2357
    Us...> {
2358
  using type =
2359
      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2360
};
2361
2362
template <class F, class... Us>
2363
using invoke_result = invoke_result_impl<F, void, Us...>;
2364
2365
template <class F, class... Us>
2366
using invoke_result_t = typename invoke_result<F, Us...>::type;
2367
2368
#if defined(_MSC_VER) && _MSC_VER <= 1900
2369
// TODO make a version which works with MSVC 2015
2370
template <class T, class U = T>
2371
struct is_swappable : std::true_type {};
2372
2373
template <class T, class U = T>
2374
struct is_nothrow_swappable : std::true_type {};
2375
#else
2376
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2377
namespace swap_adl_tests {
2378
// if swap ADL finds this then it would call std::swap otherwise (same
2379
// signature)
2380
struct tag {};
2381
2382
template <class T>
2383
tag swap(T&, T&);
2384
template <class T, std::size_t N>
2385
tag swap(T (&a)[N], T (&b)[N]);
2386
2387
// helper functions to test if an unqualified swap is possible, and if it
2388
// becomes std::swap
2389
template <class, class>
2390
std::false_type can_swap(...) noexcept(false);
2391
template <class T, class U,
2392
          class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
2393
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(),
2394
                                                    std::declval<U&>())));
2395
2396
template <class, class>
2397
std::false_type uses_std(...);
2398
template <class T, class U>
2399
std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag>
2400
uses_std(int);
2401
2402
template <class T>
2403
struct is_std_swap_noexcept
2404
    : std::integral_constant<bool,
2405
                             std::is_nothrow_move_constructible<T>::value &&
2406
                                 std::is_nothrow_move_assignable<T>::value> {};
2407
2408
template <class T, std::size_t N>
2409
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2410
2411
template <class T, class U>
2412
struct is_adl_swap_noexcept
2413
    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2414
}  // namespace swap_adl_tests
2415
2416
template <class T, class U = T>
2417
struct is_swappable
2418
    : std::integral_constant<
2419
          bool,
2420
          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2421
              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2422
               (std::is_move_assignable<T>::value &&
2423
                std::is_move_constructible<T>::value))> {};
2424
2425
template <class T, std::size_t N>
2426
struct is_swappable<T[N], T[N]>
2427
    : std::integral_constant<
2428
          bool,
2429
          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2430
              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2431
                   0))::value ||
2432
               is_swappable<T, T>::value)> {};
2433
2434
template <class T, class U = T>
2435
struct is_nothrow_swappable
2436
    : std::integral_constant<
2437
          bool,
2438
          is_swappable<T, U>::value &&
2439
              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2440
                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2441
               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2442
                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2443
#endif
2444
#endif
2445
2446
// Trait for checking if a type is a tl::expected
2447
template <class T>
2448
struct is_expected_impl : std::false_type {};
2449
template <class T, class E>
2450
struct is_expected_impl<expected<T, E>> : std::true_type {};
2451
template <class T>
2452
using is_expected = is_expected_impl<decay_t<T>>;
2453
2454
template <class T, class E, class U>
2455
using expected_enable_forward_value = detail::enable_if_t<
2456
    std::is_constructible<T, U&&>::value &&
2457
    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2458
    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2459
    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2460
2461
template <class T, class E, class U, class G, class UR, class GR>
2462
using expected_enable_from_other = detail::enable_if_t<
2463
    std::is_constructible<T, UR>::value &&
2464
    std::is_constructible<E, GR>::value &&
2465
    !std::is_constructible<T, expected<U, G>&>::value &&
2466
    !std::is_constructible<T, expected<U, G>&&>::value &&
2467
    !std::is_constructible<T, const expected<U, G>&>::value &&
2468
    !std::is_constructible<T, const expected<U, G>&&>::value &&
2469
    !std::is_convertible<expected<U, G>&, T>::value &&
2470
    !std::is_convertible<expected<U, G>&&, T>::value &&
2471
    !std::is_convertible<const expected<U, G>&, T>::value &&
2472
    !std::is_convertible<const expected<U, G>&&, T>::value>;
2473
2474
template <class T, class U>
2475
using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2476
2477
template <class T>
2478
using is_copy_constructible_or_void =
2479
    is_void_or<T, std::is_copy_constructible<T>>;
2480
2481
template <class T>
2482
using is_move_constructible_or_void =
2483
    is_void_or<T, std::is_move_constructible<T>>;
2484
2485
template <class T>
2486
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2487
2488
template <class T>
2489
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2490
2491
}  // namespace detail
2492
2493
namespace detail {
2494
struct no_init_t {};
2495
static constexpr no_init_t no_init{};
2496
2497
// Implements the storage of the values, and ensures that the destructor is
2498
// trivial if it can be.
2499
//
2500
// This specialization is for where neither `T` or `E` is trivially
2501
// destructible, so the destructors must be called on destruction of the
2502
// `expected`
2503
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2504
          bool = std::is_trivially_destructible<E>::value>
2505
struct expected_storage_base {
2506
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2507
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2508
2509
  template <class... Args,
2510
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2511
                nullptr>
2512
  constexpr expected_storage_base(in_place_t, Args&&... args)
2513
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2514
2515
  template <class U, class... Args,
2516
            detail::enable_if_t<std::is_constructible<
2517
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2518
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2519
                                  Args&&... args)
2520
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2521
  template <class... Args,
2522
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2523
                nullptr>
2524
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2525
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2526
2527
  template <class U, class... Args,
2528
            detail::enable_if_t<std::is_constructible<
2529
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2530
  constexpr explicit expected_storage_base(unexpect_t,
2531
                                           std::initializer_list<U> il,
2532
                                           Args&&... args)
2533
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2534
2535
  ~expected_storage_base() {
2536
    if (m_has_val) {
2537
      m_val.~T();
2538
    } else {
2539
      m_unexpect.~unexpected<E>();
2540
    }
2541
  }
2542
  union {
2543
    T m_val;
2544
    unexpected<E> m_unexpect;
2545
    char m_no_init;
2546
  };
2547
  bool m_has_val;
2548
};
2549
2550
// This specialization is for when both `T` and `E` are trivially-destructible,
2551
// so the destructor of the `expected` can be trivial.
2552
template <class T, class E>
2553
struct expected_storage_base<T, E, true, true> {
2554
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2555
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2556
2557
  template <class... Args,
2558
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2559
                nullptr>
2560
  constexpr expected_storage_base(in_place_t, Args&&... args)
2561
40.6k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_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_
Line
Count
Source
2561
13.5k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_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_
Line
Count
Source
2561
13.5k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_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_
Line
Count
Source
2561
13.5k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2562
2563
  template <class U, class... Args,
2564
            detail::enable_if_t<std::is_constructible<
2565
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2566
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2567
                                  Args&&... args)
2568
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2569
  template <class... Args,
2570
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2571
                nullptr>
2572
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2573
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2574
2575
  template <class U, class... Args,
2576
            detail::enable_if_t<std::is_constructible<
2577
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2578
  constexpr explicit expected_storage_base(unexpect_t,
2579
                                           std::initializer_list<U> il,
2580
                                           Args&&... args)
2581
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2582
2583
  ~expected_storage_base() = default;
2584
  union {
2585
    T m_val;
2586
    unexpected<E> m_unexpect;
2587
    char m_no_init;
2588
  };
2589
  bool m_has_val;
2590
};
2591
2592
// T is trivial, E is not.
2593
template <class T, class E>
2594
struct expected_storage_base<T, E, true, false> {
2595
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2596
  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2597
      : m_no_init(), m_has_val(false) {}
2598
2599
  template <class... Args,
2600
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2601
                nullptr>
2602
  constexpr expected_storage_base(in_place_t, Args&&... args)
2603
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2604
2605
  template <class U, class... Args,
2606
            detail::enable_if_t<std::is_constructible<
2607
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2608
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2609
                                  Args&&... args)
2610
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2611
  template <class... Args,
2612
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2613
                nullptr>
2614
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2615
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2616
2617
  template <class U, class... Args,
2618
            detail::enable_if_t<std::is_constructible<
2619
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2620
  constexpr explicit expected_storage_base(unexpect_t,
2621
                                           std::initializer_list<U> il,
2622
                                           Args&&... args)
2623
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2624
2625
  ~expected_storage_base() {
2626
    if (!m_has_val) {
2627
      m_unexpect.~unexpected<E>();
2628
    }
2629
  }
2630
2631
  union {
2632
    T m_val;
2633
    unexpected<E> m_unexpect;
2634
    char m_no_init;
2635
  };
2636
  bool m_has_val;
2637
};
2638
2639
// E is trivial, T is not.
2640
template <class T, class E>
2641
struct expected_storage_base<T, E, false, true> {
2642
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2643
71.6k
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2644
2645
  template <class... Args,
2646
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2647
                nullptr>
2648
  constexpr expected_storage_base(in_place_t, Args&&... args)
2649
222k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2649
195k
      : 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_
_ZN2tl6detail21expected_storage_baseIN3ada17url_search_paramsENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2649
13.5k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEENS7_IS9_EEEEN3ada6errorsELb0ELb1EEC2IJSB_ETnPNS2_9enable_ifIXsr3std16is_constructibleISB_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Line
Count
Source
2649
13.5k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2650
2651
  template <class U, class... Args,
2652
            detail::enable_if_t<std::is_constructible<
2653
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2654
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2655
                                  Args&&... args)
2656
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2657
  template <class... Args,
2658
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2659
                nullptr>
2660
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2661
15.3k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2661
15.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_
2662
2663
  template <class U, class... Args,
2664
            detail::enable_if_t<std::is_constructible<
2665
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2666
  constexpr explicit expected_storage_base(unexpect_t,
2667
                                           std::initializer_list<U> il,
2668
                                           Args&&... args)
2669
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2670
2671
309k
  ~expected_storage_base() {
2672
309k
    if (m_has_val) {
2673
285k
      m_val.~T();
2674
285k
    }
2675
309k
  }
Unexecuted instantiation: tl::detail::expected_storage_base<ada::url, ada::errors, false, true>::~expected_storage_base()
tl::detail::expected_storage_base<ada::url_aggregator, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2671
282k
  ~expected_storage_base() {
2672
282k
    if (m_has_val) {
2673
258k
      m_val.~T();
2674
258k
    }
2675
282k
  }
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()
tl::detail::expected_storage_base<ada::url_search_params, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2671
13.5k
  ~expected_storage_base() {
2672
13.5k
    if (m_has_val) {
2673
13.5k
      m_val.~T();
2674
13.5k
    }
2675
13.5k
  }
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()
Line
Count
Source
2671
13.5k
  ~expected_storage_base() {
2672
13.5k
    if (m_has_val) {
2673
13.5k
      m_val.~T();
2674
13.5k
    }
2675
13.5k
  }
2676
  union {
2677
    T m_val;
2678
    unexpected<E> m_unexpect;
2679
    char m_no_init;
2680
  };
2681
  bool m_has_val;
2682
};
2683
2684
// `T` is `void`, `E` is trivially-destructible
2685
template <class E>
2686
struct expected_storage_base<void, E, false, true> {
2687
#if __GNUC__ <= 5
2688
// no constexpr for GCC 4/5 bug
2689
#else
2690
  TL_EXPECTED_MSVC2015_CONSTEXPR
2691
#endif
2692
  expected_storage_base() : m_has_val(true) {}
2693
2694
  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2695
2696
  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2697
2698
  template <class... Args,
2699
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2700
                nullptr>
2701
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2702
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2703
2704
  template <class U, class... Args,
2705
            detail::enable_if_t<std::is_constructible<
2706
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2707
  constexpr explicit expected_storage_base(unexpect_t,
2708
                                           std::initializer_list<U> il,
2709
                                           Args&&... args)
2710
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2711
2712
  ~expected_storage_base() = default;
2713
  struct dummy {};
2714
  union {
2715
    unexpected<E> m_unexpect;
2716
    dummy m_val;
2717
  };
2718
  bool m_has_val;
2719
};
2720
2721
// `T` is `void`, `E` is not trivially-destructible
2722
template <class E>
2723
struct expected_storage_base<void, E, false, false> {
2724
  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2725
  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2726
2727
  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2728
2729
  template <class... Args,
2730
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2731
                nullptr>
2732
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2733
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2734
2735
  template <class U, class... Args,
2736
            detail::enable_if_t<std::is_constructible<
2737
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2738
  constexpr explicit expected_storage_base(unexpect_t,
2739
                                           std::initializer_list<U> il,
2740
                                           Args&&... args)
2741
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2742
2743
  ~expected_storage_base() {
2744
    if (!m_has_val) {
2745
      m_unexpect.~unexpected<E>();
2746
    }
2747
  }
2748
2749
  union {
2750
    unexpected<E> m_unexpect;
2751
    char m_dummy;
2752
  };
2753
  bool m_has_val;
2754
};
2755
2756
// This base class provides some handy member functions which can be used in
2757
// further derived classes
2758
template <class T, class E>
2759
struct expected_operations_base : expected_storage_base<T, E> {
2760
  using expected_storage_base<T, E>::expected_storage_base;
2761
2762
  template <class... Args>
2763
  void construct(Args&&... args) noexcept {
2764
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2765
    this->m_has_val = true;
2766
  }
2767
2768
  template <class Rhs>
2769
  // NOLINTNEXTLINE(bugprone-exception-escape)
2770
62.4k
  void construct_with(Rhs&& rhs) noexcept {
2771
62.4k
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2772
62.4k
    this->m_has_val = true;
2773
62.4k
  }
2774
2775
  template <class... Args>
2776
9.12k
  void construct_error(Args&&... args) noexcept {
2777
9.12k
    new (std::addressof(this->m_unexpect))
2778
9.12k
        unexpected<E>(std::forward<Args>(args)...);
2779
9.12k
    this->m_has_val = false;
2780
9.12k
  }
2781
2782
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2783
2784
  // These assign overloads ensure that the most efficient assignment
2785
  // implementation is used while maintaining the strong exception guarantee.
2786
  // The problematic case is where rhs has a value, but *this does not.
2787
  //
2788
  // This overload handles the case where we can just copy-construct `T`
2789
  // directly into place without throwing.
2790
  template <class U = T,
2791
            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>* =
2792
                nullptr>
2793
  void assign(const expected_operations_base& rhs) noexcept {
2794
    if (!this->m_has_val && rhs.m_has_val) {
2795
      geterr().~unexpected<E>();
2796
      construct(rhs.get());
2797
    } else {
2798
      assign_common(rhs);
2799
    }
2800
  }
2801
2802
  // This overload handles the case where we can attempt to create a copy of
2803
  // `T`, then no-throw move it into place if the copy was successful.
2804
  template <class U = T,
2805
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2806
                                std::is_nothrow_move_constructible<U>::value>* =
2807
                nullptr>
2808
  void assign(const expected_operations_base& rhs) noexcept {
2809
    if (!this->m_has_val && rhs.m_has_val) {
2810
      T tmp = rhs.get();
2811
      geterr().~unexpected<E>();
2812
      construct(std::move(tmp));
2813
    } else {
2814
      assign_common(rhs);
2815
    }
2816
  }
2817
2818
  // This overload is the worst-case, where we have to move-construct the
2819
  // unexpected value into temporary storage, then try to copy the T into place.
2820
  // If the construction succeeds, then everything is fine, but if it throws,
2821
  // then we move the old unexpected value back into place before rethrowing the
2822
  // exception.
2823
  template <class U = T,
2824
            detail::enable_if_t<
2825
                !std::is_nothrow_copy_constructible<U>::value &&
2826
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2827
  void assign(const expected_operations_base& rhs) {
2828
    if (!this->m_has_val && rhs.m_has_val) {
2829
      auto tmp = std::move(geterr());
2830
      geterr().~unexpected<E>();
2831
2832
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2833
      try {
2834
        construct(rhs.get());
2835
      } catch (...) {
2836
        geterr() = std::move(tmp);
2837
        throw;
2838
      }
2839
#else
2840
      construct(rhs.get());
2841
#endif
2842
    } else {
2843
      assign_common(rhs);
2844
    }
2845
  }
2846
2847
  // These overloads do the same as above, but for rvalues
2848
  template <class U = T,
2849
            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>* =
2850
                nullptr>
2851
  void assign(expected_operations_base&& rhs) noexcept {
2852
    if (!this->m_has_val && rhs.m_has_val) {
2853
      geterr().~unexpected<E>();
2854
      construct(std::move(rhs).get());
2855
    } else {
2856
      assign_common(std::move(rhs));
2857
    }
2858
  }
2859
2860
  template <class U = T,
2861
            detail::enable_if_t<
2862
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2863
  void assign(expected_operations_base&& rhs) {
2864
    if (!this->m_has_val && rhs.m_has_val) {
2865
      auto tmp = std::move(geterr());
2866
      geterr().~unexpected<E>();
2867
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2868
      try {
2869
        construct(std::move(rhs).get());
2870
      } catch (...) {
2871
        geterr() = std::move(tmp);
2872
        throw;
2873
      }
2874
#else
2875
      construct(std::move(rhs).get());
2876
#endif
2877
    } else {
2878
      assign_common(std::move(rhs));
2879
    }
2880
  }
2881
2882
#else
2883
2884
  // If exceptions are disabled then we can just copy-construct
2885
  void assign(const expected_operations_base& rhs) noexcept {
2886
    if (!this->m_has_val && rhs.m_has_val) {
2887
      geterr().~unexpected<E>();
2888
      construct(rhs.get());
2889
    } else {
2890
      assign_common(rhs);
2891
    }
2892
  }
2893
2894
  void assign(expected_operations_base&& rhs) noexcept {
2895
    if (!this->m_has_val && rhs.m_has_val) {
2896
      geterr().~unexpected<E>();
2897
      construct(std::move(rhs).get());
2898
    } else {
2899
      assign_common(std::move(rhs));
2900
    }
2901
  }
2902
2903
#endif
2904
2905
  // The common part of move/copy assigning
2906
  template <class Rhs>
2907
  void assign_common(Rhs&& rhs) {
2908
    if (this->m_has_val) {
2909
      if (rhs.m_has_val) {
2910
        get() = std::forward<Rhs>(rhs).get();
2911
      } else {
2912
        destroy_val();
2913
        construct_error(std::forward<Rhs>(rhs).geterr());
2914
      }
2915
    } else {
2916
      if (!rhs.m_has_val) {
2917
        geterr() = std::forward<Rhs>(rhs).geterr();
2918
      }
2919
    }
2920
  }
2921
2922
71.6k
  bool has_value() const { return this->m_has_val; }
2923
2924
  TL_EXPECTED_11_CONSTEXPR T& get() & { return this->m_val; }
2925
62.4k
  constexpr const T& get() const& { return this->m_val; }
2926
  TL_EXPECTED_11_CONSTEXPR T&& get() && { return std::move(this->m_val); }
2927
#ifndef TL_EXPECTED_NO_CONSTRR
2928
  constexpr const T&& get() const&& { return std::move(this->m_val); }
2929
#endif
2930
2931
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2932
    return this->m_unexpect;
2933
  }
2934
9.12k
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2935
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2936
    return std::move(this->m_unexpect);
2937
  }
2938
#ifndef TL_EXPECTED_NO_CONSTRR
2939
  constexpr const unexpected<E>&& geterr() const&& {
2940
    return std::move(this->m_unexpect);
2941
  }
2942
#endif
2943
2944
  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2945
};
2946
2947
// This base class provides some handy member functions which can be used in
2948
// further derived classes
2949
template <class E>
2950
struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2951
  using expected_storage_base<void, E>::expected_storage_base;
2952
2953
  template <class... Args>
2954
  void construct() noexcept {
2955
    this->m_has_val = true;
2956
  }
2957
2958
  // This function doesn't use its argument, but needs it so that code in
2959
  // levels above this can work independently of whether T is void
2960
  template <class Rhs>
2961
  void construct_with(Rhs&&) noexcept {
2962
    this->m_has_val = true;
2963
  }
2964
2965
  template <class... Args>
2966
  void construct_error(Args&&... args) noexcept {
2967
    new (std::addressof(this->m_unexpect))
2968
        unexpected<E>(std::forward<Args>(args)...);
2969
    this->m_has_val = false;
2970
  }
2971
2972
  template <class Rhs>
2973
  void assign(Rhs&& rhs) noexcept {
2974
    if (!this->m_has_val) {
2975
      if (rhs.m_has_val) {
2976
        geterr().~unexpected<E>();
2977
        construct();
2978
      } else {
2979
        geterr() = std::forward<Rhs>(rhs).geterr();
2980
      }
2981
    } else {
2982
      if (!rhs.m_has_val) {
2983
        construct_error(std::forward<Rhs>(rhs).geterr());
2984
      }
2985
    }
2986
  }
2987
2988
  bool has_value() const { return this->m_has_val; }
2989
2990
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2991
    return this->m_unexpect;
2992
  }
2993
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2994
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2995
    return std::move(this->m_unexpect);
2996
  }
2997
#ifndef TL_EXPECTED_NO_CONSTRR
2998
  constexpr const unexpected<E>&& geterr() const&& {
2999
    return std::move(this->m_unexpect);
3000
  }
3001
#endif
3002
3003
  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
3004
    // no-op
3005
  }
3006
};
3007
3008
// This class manages conditionally having a trivial copy constructor
3009
// This specialization is for when T and E are trivially copy constructible
3010
template <class T, class E,
3011
          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(
3012
                                   T)>::value &&
3013
                 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
3014
struct expected_copy_base : expected_operations_base<T, E> {
3015
  using expected_operations_base<T, E>::expected_operations_base;
3016
};
3017
3018
// This specialization is for when T or E are not trivially copy constructible
3019
template <class T, class E>
3020
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
3021
  using expected_operations_base<T, E>::expected_operations_base;
3022
3023
  expected_copy_base() = default;
3024
  expected_copy_base(const expected_copy_base& rhs)
3025
71.6k
      : expected_operations_base<T, E>(no_init) {
3026
71.6k
    if (rhs.has_value()) {
3027
62.4k
      this->construct_with(rhs);
3028
62.4k
    } else {
3029
9.12k
      this->construct_error(rhs.geterr());
3030
9.12k
    }
3031
71.6k
  }
3032
3033
  expected_copy_base(expected_copy_base&& rhs) = default;
3034
  expected_copy_base& operator=(const expected_copy_base& rhs) = default;
3035
  expected_copy_base& operator=(expected_copy_base&& rhs) = default;
3036
};
3037
3038
// This class manages conditionally having a trivial move constructor
3039
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3040
// doesn't implement an analogue to std::is_trivially_move_constructible. We
3041
// have to make do with a non-trivial move constructor even if T is trivially
3042
// move constructible
3043
#ifndef TL_EXPECTED_GCC49
3044
template <class T, class E,
3045
          bool =
3046
              is_void_or<T, std::is_trivially_move_constructible<T>>::value &&
3047
              std::is_trivially_move_constructible<E>::value>
3048
struct expected_move_base : expected_copy_base<T, E> {
3049
  using expected_copy_base<T, E>::expected_copy_base;
3050
};
3051
#else
3052
template <class T, class E, bool = false>
3053
struct expected_move_base;
3054
#endif
3055
template <class T, class E>
3056
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
3057
  using expected_copy_base<T, E>::expected_copy_base;
3058
3059
  expected_move_base() = default;
3060
71.6k
  expected_move_base(const expected_move_base& rhs) = default;
3061
3062
  expected_move_base(expected_move_base&& rhs) noexcept(
3063
      std::is_nothrow_move_constructible<T>::value)
3064
      : expected_copy_base<T, E>(no_init) {
3065
    if (rhs.has_value()) {
3066
      this->construct_with(std::move(rhs));
3067
    } else {
3068
      this->construct_error(std::move(rhs.geterr()));
3069
    }
3070
  }
3071
  expected_move_base& operator=(const expected_move_base& rhs) = default;
3072
  expected_move_base& operator=(expected_move_base&& rhs) = default;
3073
};
3074
3075
// This class manages conditionally having a trivial copy assignment operator
3076
template <
3077
    class T, class E,
3078
    bool =
3079
        is_void_or<
3080
            T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
3081
                           TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
3082
                           TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value &&
3083
        TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value &&
3084
        TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value &&
3085
        TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
3086
struct expected_copy_assign_base : expected_move_base<T, E> {
3087
  using expected_move_base<T, E>::expected_move_base;
3088
};
3089
3090
template <class T, class E>
3091
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
3092
  using expected_move_base<T, E>::expected_move_base;
3093
3094
  expected_copy_assign_base() = default;
3095
71.6k
  expected_copy_assign_base(const expected_copy_assign_base& rhs) = default;
3096
3097
  expected_copy_assign_base(expected_copy_assign_base&& rhs) = default;
3098
  expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
3099
    this->assign(rhs);
3100
    return *this;
3101
  }
3102
  expected_copy_assign_base& operator=(expected_copy_assign_base&& rhs) =
3103
      default;
3104
};
3105
3106
// This class manages conditionally having a trivial move assignment operator
3107
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3108
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
3109
// to make do with a non-trivial move assignment operator even if T is trivially
3110
// move assignable
3111
#ifndef TL_EXPECTED_GCC49
3112
template <
3113
    class T, class E,
3114
    bool = is_void_or<
3115
               T, conjunction<std::is_trivially_destructible<T>,
3116
                              std::is_trivially_move_constructible<T>,
3117
                              std::is_trivially_move_assignable<T>>>::value &&
3118
           std::is_trivially_destructible<E>::value &&
3119
           std::is_trivially_move_constructible<E>::value &&
3120
           std::is_trivially_move_assignable<E>::value>
3121
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
3122
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3123
};
3124
#else
3125
template <class T, class E, bool = false>
3126
struct expected_move_assign_base;
3127
#endif
3128
3129
template <class T, class E>
3130
struct expected_move_assign_base<T, E, false>
3131
    : expected_copy_assign_base<T, E> {
3132
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3133
3134
  expected_move_assign_base() = default;
3135
71.6k
  expected_move_assign_base(const expected_move_assign_base& rhs) = default;
3136
3137
  expected_move_assign_base(expected_move_assign_base&& rhs) = default;
3138
3139
  expected_move_assign_base& operator=(const expected_move_assign_base& rhs) =
3140
      default;
3141
3142
  expected_move_assign_base&
3143
  operator=(expected_move_assign_base&& rhs) noexcept(
3144
      std::is_nothrow_move_constructible<T>::value &&
3145
      std::is_nothrow_move_assignable<T>::value) {
3146
    this->assign(std::move(rhs));
3147
    return *this;
3148
  }
3149
};
3150
3151
// expected_delete_ctor_base will conditionally delete copy and move
3152
// constructors depending on whether T is copy/move constructible
3153
template <class T, class E,
3154
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3155
                             std::is_copy_constructible<E>::value),
3156
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3157
                             std::is_move_constructible<E>::value)>
3158
struct expected_delete_ctor_base {
3159
  expected_delete_ctor_base() = default;
3160
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3161
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3162
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3163
      default;
3164
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3165
      default;
3166
};
3167
3168
template <class T, class E>
3169
struct expected_delete_ctor_base<T, E, true, false> {
3170
  expected_delete_ctor_base() = default;
3171
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3172
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3173
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3174
      default;
3175
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3176
      default;
3177
};
3178
3179
template <class T, class E>
3180
struct expected_delete_ctor_base<T, E, false, true> {
3181
  expected_delete_ctor_base() = default;
3182
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3183
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3184
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3185
      default;
3186
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3187
      default;
3188
};
3189
3190
template <class T, class E>
3191
struct expected_delete_ctor_base<T, E, false, false> {
3192
  expected_delete_ctor_base() = default;
3193
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3194
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3195
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3196
      default;
3197
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3198
      default;
3199
};
3200
3201
// expected_delete_assign_base will conditionally delete copy and move
3202
// constructors depending on whether T and E are copy/move constructible +
3203
// assignable
3204
template <class T, class E,
3205
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3206
                             std::is_copy_constructible<E>::value &&
3207
                             is_copy_assignable_or_void<T>::value &&
3208
                             std::is_copy_assignable<E>::value),
3209
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3210
                             std::is_move_constructible<E>::value &&
3211
                             is_move_assignable_or_void<T>::value &&
3212
                             std::is_move_assignable<E>::value)>
3213
struct expected_delete_assign_base {
3214
  expected_delete_assign_base() = default;
3215
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3216
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3217
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3218
      default;
3219
  expected_delete_assign_base& operator=(
3220
      expected_delete_assign_base&&) noexcept = default;
3221
};
3222
3223
template <class T, class E>
3224
struct expected_delete_assign_base<T, E, true, false> {
3225
  expected_delete_assign_base() = default;
3226
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3227
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3228
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3229
      default;
3230
  expected_delete_assign_base& operator=(
3231
      expected_delete_assign_base&&) noexcept = delete;
3232
};
3233
3234
template <class T, class E>
3235
struct expected_delete_assign_base<T, E, false, true> {
3236
  expected_delete_assign_base() = default;
3237
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3238
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3239
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3240
      delete;
3241
  expected_delete_assign_base& operator=(
3242
      expected_delete_assign_base&&) noexcept = default;
3243
};
3244
3245
template <class T, class E>
3246
struct expected_delete_assign_base<T, E, false, false> {
3247
  expected_delete_assign_base() = default;
3248
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3249
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3250
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3251
      delete;
3252
  expected_delete_assign_base& operator=(
3253
      expected_delete_assign_base&&) noexcept = delete;
3254
};
3255
3256
// This is needed to be able to construct the expected_default_ctor_base which
3257
// follows, while still conditionally deleting the default constructor.
3258
struct default_constructor_tag {
3259
  explicit constexpr default_constructor_tag() = default;
3260
};
3261
3262
// expected_default_ctor_base will ensure that expected has a deleted default
3263
// consturctor if T is not default constructible.
3264
// This specialization is for when T is default constructible
3265
template <class T, class E,
3266
          bool Enable =
3267
              std::is_default_constructible<T>::value || std::is_void<T>::value>
3268
struct expected_default_ctor_base {
3269
  constexpr expected_default_ctor_base() noexcept = default;
3270
  constexpr expected_default_ctor_base(
3271
      expected_default_ctor_base const&) noexcept = default;
3272
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3273
      default;
3274
  expected_default_ctor_base& operator=(
3275
      expected_default_ctor_base const&) noexcept = default;
3276
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3277
      default;
3278
3279
278k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
tl::detail::expected_default_ctor_base<ada::url_aggregator, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3279
211k
  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)
tl::detail::expected_default_ctor_base<ada::url_search_params, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3279
13.5k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
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)
Line
Count
Source
3279
13.5k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
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)
Line
Count
Source
3279
13.5k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
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)
Line
Count
Source
3279
13.5k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
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)
Line
Count
Source
3279
13.5k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3280
};
3281
3282
// This specialization is for when T is not default constructible
3283
template <class T, class E>
3284
struct expected_default_ctor_base<T, E, false> {
3285
  constexpr expected_default_ctor_base() noexcept = delete;
3286
  constexpr expected_default_ctor_base(
3287
      expected_default_ctor_base const&) noexcept = default;
3288
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3289
      default;
3290
  expected_default_ctor_base& operator=(
3291
      expected_default_ctor_base const&) noexcept = default;
3292
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3293
      default;
3294
3295
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3296
};
3297
}  // namespace detail
3298
3299
template <class E>
3300
class bad_expected_access : public std::exception {
3301
 public:
3302
0
  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3303
3304
0
  virtual const char* what() const noexcept override {
3305
0
    return "Bad expected access";
3306
0
  }
3307
3308
  const E& error() const& { return m_val; }
3309
  E& error() & { return m_val; }
3310
  const E&& error() const&& { return std::move(m_val); }
3311
  E&& error() && { return std::move(m_val); }
3312
3313
 private:
3314
  E m_val;
3315
};
3316
3317
/// An `expected<T, E>` object is an object that contains the storage for
3318
/// another object and manages the lifetime of this contained object `T`.
3319
/// Alternatively it could contain the storage for another unexpected object
3320
/// `E`. The contained object may not be initialized after the expected object
3321
/// has been initialized, and may not be destroyed before the expected object
3322
/// has been destroyed. The initialization state of the contained object is
3323
/// tracked by the expected object.
3324
template <class T, class E>
3325
class expected : private detail::expected_move_assign_base<T, E>,
3326
                 private detail::expected_delete_ctor_base<T, E>,
3327
                 private detail::expected_delete_assign_base<T, E>,
3328
                 private detail::expected_default_ctor_base<T, E> {
3329
  static_assert(!std::is_reference<T>::value, "T must not be a reference");
3330
  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3331
                "T must not be in_place_t");
3332
  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3333
                "T must not be unexpect_t");
3334
  static_assert(
3335
      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3336
      "T must not be unexpected<E>");
3337
  static_assert(!std::is_reference<E>::value, "E must not be a reference");
3338
3339
2.96M
  T* valptr() { return std::addressof(this->m_val); }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::valptr()
tl::expected<ada::url_aggregator, ada::errors>::valptr()
Line
Count
Source
3339
2.55M
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<ada::url_search_params, ada::errors>::valptr()
Line
Count
Source
3339
203k
  T* valptr() { return std::addressof(this->m_val); }
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()
Line
Count
Source
3339
27.1k
  T* valptr() { return std::addressof(this->m_val); }
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()
Line
Count
Source
3339
60.2k
  T* valptr() { return std::addressof(this->m_val); }
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()
Line
Count
Source
3339
60.2k
  T* valptr() { return std::addressof(this->m_val); }
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()
Line
Count
Source
3339
60.2k
  T* valptr() { return std::addressof(this->m_val); }
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::valptr()
3340
  const T* valptr() const { return std::addressof(this->m_val); }
3341
  unexpected<E>* errptr() { return std::addressof(this->m_unexpect); }
3342
  const unexpected<E>* errptr() const {
3343
    return std::addressof(this->m_unexpect);
3344
  }
3345
3346
  template <class U = T,
3347
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3348
66.9k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3349
66.9k
    return this->m_val;
3350
66.9k
  }
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3348
66.9k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3349
66.9k
    return this->m_val;
3350
66.9k
  }
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEE3valIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
3351
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<ada::url_aggregator, ada::errors>::err()
Unexecuted instantiation: tl::expected<ada::url_pattern_init, 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()
3352
3353
  template <class U = T,
3354
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3355
  constexpr const U& val() const {
3356
    return this->m_val;
3357
  }
3358
  constexpr const unexpected<E>& err() const { return this->m_unexpect; }
3359
3360
  using impl_base = detail::expected_move_assign_base<T, E>;
3361
  using ctor_base = detail::expected_default_ctor_base<T, E>;
3362
3363
 public:
3364
  typedef T value_type;
3365
  typedef E error_type;
3366
  typedef unexpected<E> unexpected_type;
3367
3368
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3369
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3370
  template <class F>
3371
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & {
3372
    return and_then_impl(*this, std::forward<F>(f));
3373
  }
3374
  template <class F>
3375
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && {
3376
    return and_then_impl(std::move(*this), std::forward<F>(f));
3377
  }
3378
  template <class F>
3379
  constexpr auto and_then(F&& f) const& {
3380
    return and_then_impl(*this, std::forward<F>(f));
3381
  }
3382
3383
#ifndef TL_EXPECTED_NO_CONSTRR
3384
  template <class F>
3385
  constexpr auto and_then(F&& f) const&& {
3386
    return and_then_impl(std::move(*this), std::forward<F>(f));
3387
  }
3388
#endif
3389
3390
#else
3391
  template <class F>
3392
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & -> decltype(and_then_impl(
3393
      std::declval<expected&>(), std::forward<F>(f))) {
3394
    return and_then_impl(*this, std::forward<F>(f));
3395
  }
3396
  template <class F>
3397
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && -> decltype(and_then_impl(
3398
      std::declval<expected&&>(), std::forward<F>(f))) {
3399
    return and_then_impl(std::move(*this), std::forward<F>(f));
3400
  }
3401
  template <class F>
3402
  constexpr auto and_then(F&& f) const& -> decltype(and_then_impl(
3403
      std::declval<expected const&>(), std::forward<F>(f))) {
3404
    return and_then_impl(*this, std::forward<F>(f));
3405
  }
3406
3407
#ifndef TL_EXPECTED_NO_CONSTRR
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(std::move(*this), std::forward<F>(f));
3412
  }
3413
#endif
3414
#endif
3415
3416
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3417
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3418
  template <class F>
3419
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) & {
3420
    return expected_map_impl(*this, std::forward<F>(f));
3421
  }
3422
  template <class F>
3423
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) && {
3424
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3425
  }
3426
  template <class F>
3427
  constexpr auto map(F&& f) const& {
3428
    return expected_map_impl(*this, std::forward<F>(f));
3429
  }
3430
  template <class F>
3431
  constexpr auto map(F&& f) const&& {
3432
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3433
  }
3434
#else
3435
  template <class F>
3436
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3437
      std::declval<expected&>(), std::declval<F&&>())) map(F&& f) & {
3438
    return expected_map_impl(*this, std::forward<F>(f));
3439
  }
3440
  template <class F>
3441
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3442
                                                      std::declval<F&&>()))
3443
  map(F&& f) && {
3444
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3445
  }
3446
  template <class F>
3447
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3448
                                       std::declval<F&&>()))
3449
  map(F&& f) const& {
3450
    return expected_map_impl(*this, std::forward<F>(f));
3451
  }
3452
3453
#ifndef TL_EXPECTED_NO_CONSTRR
3454
  template <class F>
3455
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3456
                                       std::declval<F&&>())) map(F&& f)
3457
      const&& {
3458
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3459
  }
3460
#endif
3461
#endif
3462
3463
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3464
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3465
  template <class F>
3466
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) & {
3467
    return expected_map_impl(*this, std::forward<F>(f));
3468
  }
3469
  template <class F>
3470
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) && {
3471
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3472
  }
3473
  template <class F>
3474
  constexpr auto transform(F&& f) const& {
3475
    return expected_map_impl(*this, std::forward<F>(f));
3476
  }
3477
  template <class F>
3478
  constexpr auto transform(F&& f) const&& {
3479
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3480
  }
3481
#else
3482
  template <class F>
3483
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3484
      std::declval<expected&>(), std::declval<F&&>())) transform(F&& f) & {
3485
    return expected_map_impl(*this, std::forward<F>(f));
3486
  }
3487
  template <class F>
3488
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3489
                                                      std::declval<F&&>()))
3490
  transform(F&& f) && {
3491
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3492
  }
3493
  template <class F>
3494
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3495
                                       std::declval<F&&>()))
3496
  transform(F&& f) const& {
3497
    return expected_map_impl(*this, std::forward<F>(f));
3498
  }
3499
3500
#ifndef TL_EXPECTED_NO_CONSTRR
3501
  template <class F>
3502
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3503
                                       std::declval<F&&>())) transform(F&& f)
3504
      const&& {
3505
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3506
  }
3507
#endif
3508
#endif
3509
3510
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3511
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3512
  template <class F>
3513
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) & {
3514
    return map_error_impl(*this, std::forward<F>(f));
3515
  }
3516
  template <class F>
3517
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) && {
3518
    return map_error_impl(std::move(*this), std::forward<F>(f));
3519
  }
3520
  template <class F>
3521
  constexpr auto map_error(F&& f) const& {
3522
    return map_error_impl(*this, std::forward<F>(f));
3523
  }
3524
  template <class F>
3525
  constexpr auto map_error(F&& f) const&& {
3526
    return map_error_impl(std::move(*this), std::forward<F>(f));
3527
  }
3528
#else
3529
  template <class F>
3530
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3531
      std::declval<expected&>(), std::declval<F&&>())) map_error(F&& f) & {
3532
    return map_error_impl(*this, std::forward<F>(f));
3533
  }
3534
  template <class F>
3535
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3536
                                                   std::declval<F&&>()))
3537
  map_error(F&& f) && {
3538
    return map_error_impl(std::move(*this), std::forward<F>(f));
3539
  }
3540
  template <class F>
3541
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3542
                                    std::declval<F&&>()))
3543
  map_error(F&& f) const& {
3544
    return map_error_impl(*this, std::forward<F>(f));
3545
  }
3546
3547
#ifndef TL_EXPECTED_NO_CONSTRR
3548
  template <class F>
3549
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3550
                                    std::declval<F&&>())) map_error(F&& f)
3551
      const&& {
3552
    return map_error_impl(std::move(*this), std::forward<F>(f));
3553
  }
3554
#endif
3555
#endif
3556
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3557
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3558
  template <class F>
3559
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) & {
3560
    return map_error_impl(*this, std::forward<F>(f));
3561
  }
3562
  template <class F>
3563
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) && {
3564
    return map_error_impl(std::move(*this), std::forward<F>(f));
3565
  }
3566
  template <class F>
3567
  constexpr auto transform_error(F&& f) const& {
3568
    return map_error_impl(*this, std::forward<F>(f));
3569
  }
3570
  template <class F>
3571
  constexpr auto transform_error(F&& f) const&& {
3572
    return map_error_impl(std::move(*this), std::forward<F>(f));
3573
  }
3574
#else
3575
  template <class F>
3576
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3577
      std::declval<expected&>(),
3578
      std::declval<F&&>())) transform_error(F&& f) & {
3579
    return map_error_impl(*this, std::forward<F>(f));
3580
  }
3581
  template <class F>
3582
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3583
                                                   std::declval<F&&>()))
3584
  transform_error(F&& f) && {
3585
    return map_error_impl(std::move(*this), std::forward<F>(f));
3586
  }
3587
  template <class F>
3588
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3589
                                    std::declval<F&&>()))
3590
  transform_error(F&& f) const& {
3591
    return map_error_impl(*this, std::forward<F>(f));
3592
  }
3593
3594
#ifndef TL_EXPECTED_NO_CONSTRR
3595
  template <class F>
3596
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3597
                                    std::declval<F&&>())) transform_error(F&& f)
3598
      const&& {
3599
    return map_error_impl(std::move(*this), std::forward<F>(f));
3600
  }
3601
#endif
3602
#endif
3603
  template <class F>
3604
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) & {
3605
    return or_else_impl(*this, std::forward<F>(f));
3606
  }
3607
3608
  template <class F>
3609
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) && {
3610
    return or_else_impl(std::move(*this), std::forward<F>(f));
3611
  }
3612
3613
  template <class F>
3614
  expected constexpr or_else(F&& f) const& {
3615
    return or_else_impl(*this, std::forward<F>(f));
3616
  }
3617
3618
#ifndef TL_EXPECTED_NO_CONSTRR
3619
  template <class F>
3620
  expected constexpr or_else(F&& f) const&& {
3621
    return or_else_impl(std::move(*this), std::forward<F>(f));
3622
  }
3623
#endif
3624
  constexpr expected() = default;
3625
71.6k
  constexpr expected(const expected& rhs) = default;
3626
  constexpr expected(expected&& rhs) = default;
3627
  expected& operator=(const expected& rhs) = default;
3628
  expected& operator=(expected&& rhs) = default;
3629
3630
  template <class... Args,
3631
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
3632
                nullptr>
3633
  constexpr expected(in_place_t, Args&&... args)
3634
263k
      : impl_base(in_place, std::forward<Args>(args)...),
3635
263k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3634
195k
      : impl_base(in_place, std::forward<Args>(args)...),
3635
195k
        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_
_ZN2tl8expectedIN3ada17url_search_paramsENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3634
13.5k
      : impl_base(in_place, std::forward<Args>(args)...),
3635
13.5k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS6_IS8_EEEEN3ada6errorsEEC2IJSA_ETnPNS1_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Line
Count
Source
3634
13.5k
      : impl_base(in_place, std::forward<Args>(args)...),
3635
13.5k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE0EEENS1_6errorsEEC2IJS9_ETnPNS3_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
3634
13.5k
      : impl_base(in_place, std::forward<Args>(args)...),
3635
13.5k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE1EEENS1_6errorsEEC2IJS9_ETnPNS3_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
3634
13.5k
      : impl_base(in_place, std::forward<Args>(args)...),
3635
13.5k
        ctor_base(detail::default_constructor_tag{}) {}
_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_
Line
Count
Source
3634
13.5k
      : impl_base(in_place, std::forward<Args>(args)...),
3635
13.5k
        ctor_base(detail::default_constructor_tag{}) {}
3636
3637
  template <class U, class... Args,
3638
            detail::enable_if_t<std::is_constructible<
3639
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3640
  constexpr expected(in_place_t, std::initializer_list<U> il, Args&&... args)
3641
      : impl_base(in_place, il, std::forward<Args>(args)...),
3642
        ctor_base(detail::default_constructor_tag{}) {}
3643
3644
  template <
3645
      class G = E,
3646
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3647
      detail::enable_if_t<!std::is_convertible<const G&, E>::value>* = nullptr>
3648
  explicit constexpr expected(const unexpected<G>& e)
3649
      : impl_base(unexpect, e.value()),
3650
        ctor_base(detail::default_constructor_tag{}) {}
3651
3652
  template <
3653
      class G = E,
3654
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3655
      detail::enable_if_t<std::is_convertible<const G&, E>::value>* = nullptr>
3656
  constexpr expected(unexpected<G> const& e)
3657
      : impl_base(unexpect, e.value()),
3658
        ctor_base(detail::default_constructor_tag{}) {}
3659
3660
  template <
3661
      class G = E,
3662
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3663
      detail::enable_if_t<!std::is_convertible<G&&, E>::value>* = nullptr>
3664
  explicit constexpr expected(unexpected<G>&& e) noexcept(
3665
      std::is_nothrow_constructible<E, G&&>::value)
3666
      : impl_base(unexpect, std::move(e.value())),
3667
        ctor_base(detail::default_constructor_tag{}) {}
3668
3669
  template <
3670
      class G = E,
3671
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3672
      detail::enable_if_t<std::is_convertible<G&&, E>::value>* = nullptr>
3673
  constexpr expected(unexpected<G>&& e) noexcept(
3674
      std::is_nothrow_constructible<E, G&&>::value)
3675
15.3k
      : impl_base(unexpect, std::move(e.value())),
3676
15.3k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3675
15.3k
      : impl_base(unexpect, std::move(e.value())),
3676
15.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
3677
3678
  template <class... Args,
3679
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
3680
                nullptr>
3681
  constexpr explicit expected(unexpect_t, Args&&... args)
3682
      : impl_base(unexpect, std::forward<Args>(args)...),
3683
        ctor_base(detail::default_constructor_tag{}) {}
3684
3685
  template <class U, class... Args,
3686
            detail::enable_if_t<std::is_constructible<
3687
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3688
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3689
                              Args&&... args)
3690
      : impl_base(unexpect, il, std::forward<Args>(args)...),
3691
        ctor_base(detail::default_constructor_tag{}) {}
3692
3693
  template <class U, class G,
3694
            detail::enable_if_t<!(std::is_convertible<U const&, T>::value &&
3695
                                  std::is_convertible<G const&, E>::value)>* =
3696
                nullptr,
3697
            detail::expected_enable_from_other<T, E, U, G, const U&,
3698
                                               const G&>* = nullptr>
3699
  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3700
      : ctor_base(detail::default_constructor_tag{}) {
3701
    if (rhs.has_value()) {
3702
      this->construct(*rhs);
3703
    } else {
3704
      this->construct_error(rhs.error());
3705
    }
3706
  }
3707
3708
  template <
3709
      class U, class G,
3710
      detail::enable_if_t<(std::is_convertible<U const&, T>::value &&
3711
                           std::is_convertible<G const&, E>::value)>* = nullptr,
3712
      detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* =
3713
          nullptr>
3714
  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3715
      : ctor_base(detail::default_constructor_tag{}) {
3716
    if (rhs.has_value()) {
3717
      this->construct(*rhs);
3718
    } else {
3719
      this->construct_error(rhs.error());
3720
    }
3721
  }
3722
3723
  template <
3724
      class U, class G,
3725
      detail::enable_if_t<!(std::is_convertible<U&&, T>::value &&
3726
                            std::is_convertible<G&&, E>::value)>* = nullptr,
3727
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3728
  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3729
      : ctor_base(detail::default_constructor_tag{}) {
3730
    if (rhs.has_value()) {
3731
      this->construct(std::move(*rhs));
3732
    } else {
3733
      this->construct_error(std::move(rhs.error()));
3734
    }
3735
  }
3736
3737
  template <
3738
      class U, class G,
3739
      detail::enable_if_t<(std::is_convertible<U&&, T>::value &&
3740
                           std::is_convertible<G&&, E>::value)>* = nullptr,
3741
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3742
  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3743
      : ctor_base(detail::default_constructor_tag{}) {
3744
    if (rhs.has_value()) {
3745
      this->construct(std::move(*rhs));
3746
    } else {
3747
      this->construct_error(std::move(rhs.error()));
3748
    }
3749
  }
3750
3751
  template <class U = T,
3752
            detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr,
3753
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3754
  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3755
      : expected(in_place, std::forward<U>(v)) {}
3756
3757
  template <class U = T,
3758
            detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr,
3759
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3760
  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3761
263k
      : expected(in_place, std::forward<U>(v)) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3761
195k
      : 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_
_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_
Line
Count
Source
3761
13.5k
      : expected(in_place, std::forward<U>(v)) {}
_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_
Line
Count
Source
3761
13.5k
      : expected(in_place, std::forward<U>(v)) {}
_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_
Line
Count
Source
3761
13.5k
      : expected(in_place, std::forward<U>(v)) {}
_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_
Line
Count
Source
3761
13.5k
      : expected(in_place, std::forward<U>(v)) {}
_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_
Line
Count
Source
3761
13.5k
      : expected(in_place, std::forward<U>(v)) {}
3762
3763
  template <
3764
      class U = T, class G = T,
3765
      detail::enable_if_t<std::is_nothrow_constructible<T, U&&>::value>* =
3766
          nullptr,
3767
      detail::enable_if_t<!std::is_void<G>::value>* = nullptr,
3768
      detail::enable_if_t<
3769
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3770
           !detail::conjunction<std::is_scalar<T>,
3771
                                std::is_same<T, detail::decay_t<U>>>::value &&
3772
           std::is_constructible<T, U>::value &&
3773
           std::is_assignable<G&, U>::value &&
3774
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3775
  expected& operator=(U&& v) {
3776
    if (has_value()) {
3777
      val() = std::forward<U>(v);
3778
    } else {
3779
      err().~unexpected<E>();
3780
      ::new (valptr()) T(std::forward<U>(v));
3781
      this->m_has_val = true;
3782
    }
3783
3784
    return *this;
3785
  }
3786
3787
  template <
3788
      class U = T, class G = T,
3789
      detail::enable_if_t<!std::is_nothrow_constructible<T, U&&>::value>* =
3790
          nullptr,
3791
      detail::enable_if_t<!std::is_void<U>::value>* = nullptr,
3792
      detail::enable_if_t<
3793
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3794
           !detail::conjunction<std::is_scalar<T>,
3795
                                std::is_same<T, detail::decay_t<U>>>::value &&
3796
           std::is_constructible<T, U>::value &&
3797
           std::is_assignable<G&, U>::value &&
3798
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3799
  expected& operator=(U&& v) {
3800
    if (has_value()) {
3801
      val() = std::forward<U>(v);
3802
    } else {
3803
      auto tmp = std::move(err());
3804
      err().~unexpected<E>();
3805
3806
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3807
      try {
3808
        ::new (valptr()) T(std::forward<U>(v));
3809
        this->m_has_val = true;
3810
      } catch (...) {
3811
        err() = std::move(tmp);
3812
        throw;
3813
      }
3814
#else
3815
      ::new (valptr()) T(std::forward<U>(v));
3816
      this->m_has_val = true;
3817
#endif
3818
    }
3819
3820
    return *this;
3821
  }
3822
3823
  template <class G = E,
3824
            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3825
                                std::is_assignable<G&, G>::value>* = nullptr>
3826
  expected& operator=(const unexpected<G>& rhs) {
3827
    if (!has_value()) {
3828
      err() = rhs;
3829
    } else {
3830
      this->destroy_val();
3831
      ::new (errptr()) unexpected<E>(rhs);
3832
      this->m_has_val = false;
3833
    }
3834
3835
    return *this;
3836
  }
3837
3838
  template <class G = E,
3839
            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3840
                                std::is_move_assignable<G>::value>* = nullptr>
3841
  expected& operator=(unexpected<G>&& rhs) noexcept {
3842
    if (!has_value()) {
3843
      err() = std::move(rhs);
3844
    } else {
3845
      this->destroy_val();
3846
      ::new (errptr()) unexpected<E>(std::move(rhs));
3847
      this->m_has_val = false;
3848
    }
3849
3850
    return *this;
3851
  }
3852
3853
  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3854
                               T, Args&&...>::value>* = nullptr>
3855
  void emplace(Args&&... args) {
3856
    if (has_value()) {
3857
      val().~T();
3858
    } else {
3859
      err().~unexpected<E>();
3860
      this->m_has_val = true;
3861
    }
3862
    ::new (valptr()) T(std::forward<Args>(args)...);
3863
  }
3864
3865
  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3866
                               T, Args&&...>::value>* = nullptr>
3867
  void emplace(Args&&... args) {
3868
    if (has_value()) {
3869
      val().~T();
3870
      ::new (valptr()) T(std::forward<Args>(args)...);
3871
    } else {
3872
      auto tmp = std::move(err());
3873
      err().~unexpected<E>();
3874
3875
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3876
      try {
3877
        ::new (valptr()) T(std::forward<Args>(args)...);
3878
        this->m_has_val = true;
3879
      } catch (...) {
3880
        err() = std::move(tmp);
3881
        throw;
3882
      }
3883
#else
3884
      ::new (valptr()) T(std::forward<Args>(args)...);
3885
      this->m_has_val = true;
3886
#endif
3887
    }
3888
  }
3889
3890
  template <class U, class... Args,
3891
            detail::enable_if_t<std::is_nothrow_constructible<
3892
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3893
  void emplace(std::initializer_list<U> il, Args&&... args) {
3894
    if (has_value()) {
3895
      T t(il, std::forward<Args>(args)...);
3896
      val() = std::move(t);
3897
    } else {
3898
      err().~unexpected<E>();
3899
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3900
      this->m_has_val = true;
3901
    }
3902
  }
3903
3904
  template <class U, class... Args,
3905
            detail::enable_if_t<!std::is_nothrow_constructible<
3906
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3907
  void emplace(std::initializer_list<U> il, Args&&... args) {
3908
    if (has_value()) {
3909
      T t(il, std::forward<Args>(args)...);
3910
      val() = std::move(t);
3911
    } else {
3912
      auto tmp = std::move(err());
3913
      err().~unexpected<E>();
3914
3915
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3916
      try {
3917
        ::new (valptr()) T(il, std::forward<Args>(args)...);
3918
        this->m_has_val = true;
3919
      } catch (...) {
3920
        err() = std::move(tmp);
3921
        throw;
3922
      }
3923
#else
3924
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3925
      this->m_has_val = true;
3926
#endif
3927
    }
3928
  }
3929
3930
 private:
3931
  using t_is_void = std::true_type;
3932
  using t_is_not_void = std::false_type;
3933
  using t_is_nothrow_move_constructible = std::true_type;
3934
  using move_constructing_t_can_throw = std::false_type;
3935
  using e_is_nothrow_move_constructible = std::true_type;
3936
  using move_constructing_e_can_throw = std::false_type;
3937
3938
  void swap_where_both_have_value(expected& /*rhs*/, t_is_void) noexcept {
3939
    // swapping void is a no-op
3940
  }
3941
3942
  void swap_where_both_have_value(expected& rhs, t_is_not_void) {
3943
    using std::swap;
3944
    swap(val(), rhs.val());
3945
  }
3946
3947
  void swap_where_only_one_has_value(expected& rhs, t_is_void) noexcept(
3948
      std::is_nothrow_move_constructible<E>::value) {
3949
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3950
    rhs.err().~unexpected_type();
3951
    std::swap(this->m_has_val, rhs.m_has_val);
3952
  }
3953
3954
  void swap_where_only_one_has_value(expected& rhs, t_is_not_void) {
3955
    swap_where_only_one_has_value_and_t_is_not_void(
3956
        rhs, typename std::is_nothrow_move_constructible<T>::type{},
3957
        typename std::is_nothrow_move_constructible<E>::type{});
3958
  }
3959
3960
  void swap_where_only_one_has_value_and_t_is_not_void(
3961
      expected& rhs, t_is_nothrow_move_constructible,
3962
      e_is_nothrow_move_constructible) noexcept {
3963
    auto temp = std::move(val());
3964
    val().~T();
3965
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3966
    rhs.err().~unexpected_type();
3967
    ::new (rhs.valptr()) T(std::move(temp));
3968
    std::swap(this->m_has_val, rhs.m_has_val);
3969
  }
3970
3971
  void swap_where_only_one_has_value_and_t_is_not_void(
3972
      expected& rhs, t_is_nothrow_move_constructible,
3973
      move_constructing_e_can_throw) {
3974
    auto temp = std::move(val());
3975
    val().~T();
3976
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3977
    try {
3978
      ::new (errptr()) unexpected_type(std::move(rhs.err()));
3979
      rhs.err().~unexpected_type();
3980
      ::new (rhs.valptr()) T(std::move(temp));
3981
      std::swap(this->m_has_val, rhs.m_has_val);
3982
    } catch (...) {
3983
      val() = std::move(temp);
3984
      throw;
3985
    }
3986
#else
3987
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3988
    rhs.err().~unexpected_type();
3989
    ::new (rhs.valptr()) T(std::move(temp));
3990
    std::swap(this->m_has_val, rhs.m_has_val);
3991
#endif
3992
  }
3993
3994
  void swap_where_only_one_has_value_and_t_is_not_void(
3995
      expected& rhs, move_constructing_t_can_throw,
3996
      e_is_nothrow_move_constructible) {
3997
    auto temp = std::move(rhs.err());
3998
    rhs.err().~unexpected_type();
3999
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
4000
    try {
4001
      ::new (rhs.valptr()) T(std::move(val()));
4002
      val().~T();
4003
      ::new (errptr()) unexpected_type(std::move(temp));
4004
      std::swap(this->m_has_val, rhs.m_has_val);
4005
    } catch (...) {
4006
      rhs.err() = std::move(temp);
4007
      throw;
4008
    }
4009
#else
4010
    ::new (rhs.valptr()) T(std::move(val()));
4011
    val().~T();
4012
    ::new (errptr()) unexpected_type(std::move(temp));
4013
    std::swap(this->m_has_val, rhs.m_has_val);
4014
#endif
4015
  }
4016
4017
 public:
4018
  template <class OT = T, class OE = E>
4019
  detail::enable_if_t<detail::is_swappable<OT>::value &&
4020
                      detail::is_swappable<OE>::value &&
4021
                      (std::is_nothrow_move_constructible<OT>::value ||
4022
                       std::is_nothrow_move_constructible<OE>::value)>
4023
  swap(expected& rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
4024
                               detail::is_nothrow_swappable<T>::value &&
4025
                               std::is_nothrow_move_constructible<E>::value &&
4026
                               detail::is_nothrow_swappable<E>::value) {
4027
    if (has_value() && rhs.has_value()) {
4028
      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
4029
    } else if (!has_value() && rhs.has_value()) {
4030
      rhs.swap(*this);
4031
    } else if (has_value()) {
4032
      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
4033
    } else {
4034
      using std::swap;
4035
      swap(err(), rhs.err());
4036
    }
4037
  }
4038
4039
  constexpr const T* operator->() const {
4040
    TL_ASSERT(has_value());
4041
    return valptr();
4042
  }
4043
2.96M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4044
2.96M
    TL_ASSERT(has_value());
4045
2.96M
    return valptr();
4046
2.96M
  }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator->()
tl::expected<ada::url_aggregator, ada::errors>::operator->()
Line
Count
Source
4043
2.55M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4044
2.55M
    TL_ASSERT(has_value());
4045
2.55M
    return valptr();
4046
2.55M
  }
tl::expected<ada::url_search_params, ada::errors>::operator->()
Line
Count
Source
4043
203k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4044
203k
    TL_ASSERT(has_value());
4045
203k
    return valptr();
4046
203k
  }
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->()
Line
Count
Source
4043
27.1k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4044
27.1k
    TL_ASSERT(has_value());
4045
27.1k
    return valptr();
4046
27.1k
  }
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->()
Line
Count
Source
4043
60.2k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4044
60.2k
    TL_ASSERT(has_value());
4045
60.2k
    return valptr();
4046
60.2k
  }
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->()
Line
Count
Source
4043
60.2k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4044
60.2k
    TL_ASSERT(has_value());
4045
60.2k
    return valptr();
4046
60.2k
  }
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->()
Line
Count
Source
4043
60.2k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4044
60.2k
    TL_ASSERT(has_value());
4045
60.2k
    return valptr();
4046
60.2k
  }
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::operator->()
4047
4048
  template <class U = T,
4049
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4050
  constexpr const U& operator*() const& {
4051
    TL_ASSERT(has_value());
4052
    return val();
4053
  }
4054
  template <class U = T,
4055
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4056
62.4k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4057
62.4k
    TL_ASSERT(has_value());
4058
62.4k
    return val();
4059
62.4k
  }
Unexecuted instantiation: _ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
4056
62.4k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4057
62.4k
    TL_ASSERT(has_value());
4058
62.4k
    return val();
4059
62.4k
  }
Unexecuted instantiation: _ZNR2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZNR2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEdeIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
4060
  template <class U = T,
4061
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4062
  constexpr const U&& operator*() const&& {
4063
    TL_ASSERT(has_value());
4064
    return std::move(val());
4065
  }
4066
  template <class U = T,
4067
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4068
  TL_EXPECTED_11_CONSTEXPR U&& operator*() && {
4069
    TL_ASSERT(has_value());
4070
    return std::move(val());
4071
  }
4072
4073
3.37M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::has_value() const
tl::expected<ada::url_aggregator, ada::errors>::has_value() const
Line
Count
Source
4073
2.95M
  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
tl::expected<ada::url_search_params, ada::errors>::has_value() const
Line
Count
Source
4073
203k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
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
Line
Count
Source
4073
27.1k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
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
Line
Count
Source
4073
60.2k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
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
Line
Count
Source
4073
60.2k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
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
Line
Count
Source
4073
60.2k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url_pattern_init, 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
4074
2.77M
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator bool() const
tl::expected<ada::url_aggregator, ada::errors>::operator bool() const
Line
Count
Source
4074
2.56M
  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
tl::expected<ada::url_search_params, ada::errors>::operator bool() const
Line
Count
Source
4074
203k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url_pattern_init, 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
4075
4076
  template <class U = T,
4077
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4078
  TL_EXPECTED_11_CONSTEXPR const U& value() const& {
4079
    if (!has_value())
4080
      detail::throw_exception(bad_expected_access<E>(err().value()));
4081
    return val();
4082
  }
4083
  template <class U = T,
4084
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4085
4.42k
  TL_EXPECTED_11_CONSTEXPR U& value() & {
4086
4.42k
    if (!has_value())
4087
0
      detail::throw_exception(bad_expected_access<E>(err().value()));
4088
4.42k
    return val();
4089
4.42k
  }
4090
  template <class U = T,
4091
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4092
  TL_EXPECTED_11_CONSTEXPR const U&& value() const&& {
4093
    if (!has_value())
4094
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4095
    return std::move(val());
4096
  }
4097
  template <class U = T,
4098
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4099
  TL_EXPECTED_11_CONSTEXPR U&& value() && {
4100
    if (!has_value())
4101
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4102
    return std::move(val());
4103
  }
4104
4105
  constexpr const E& error() const& {
4106
    TL_ASSERT(!has_value());
4107
    return err().value();
4108
  }
4109
0
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4110
0
    TL_ASSERT(!has_value());
4111
0
    return err().value();
4112
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<ada::url_pattern_init, 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() &
4113
  constexpr const E&& error() const&& {
4114
    TL_ASSERT(!has_value());
4115
    return std::move(err().value());
4116
  }
4117
  TL_EXPECTED_11_CONSTEXPR E&& error() && {
4118
    TL_ASSERT(!has_value());
4119
    return std::move(err().value());
4120
  }
4121
4122
  template <class U>
4123
  constexpr T value_or(U&& v) const& {
4124
    static_assert(std::is_copy_constructible<T>::value &&
4125
                      std::is_convertible<U&&, T>::value,
4126
                  "T must be copy-constructible and convertible to from U&&");
4127
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
4128
  }
4129
  template <class U>
4130
  TL_EXPECTED_11_CONSTEXPR T value_or(U&& v) && {
4131
    static_assert(std::is_move_constructible<T>::value &&
4132
                      std::is_convertible<U&&, T>::value,
4133
                  "T must be move-constructible and convertible to from U&&");
4134
    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
4135
  }
4136
};
4137
4138
namespace detail {
4139
template <class Exp>
4140
using exp_t = typename detail::decay_t<Exp>::value_type;
4141
template <class Exp>
4142
using err_t = typename detail::decay_t<Exp>::error_type;
4143
template <class Exp, class Ret>
4144
using ret_t = expected<Ret, err_t<Exp>>;
4145
4146
#ifdef TL_EXPECTED_CXX14
4147
template <class Exp, class F,
4148
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4149
          class Ret = decltype(detail::invoke(std::declval<F>(),
4150
                                              *std::declval<Exp>()))>
4151
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4152
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4153
4154
  return exp.has_value()
4155
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4156
             : Ret(unexpect, std::forward<Exp>(exp).error());
4157
}
4158
4159
template <class Exp, class F,
4160
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4161
          class Ret = decltype(detail::invoke(std::declval<F>()))>
4162
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4163
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4164
4165
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4166
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4167
}
4168
#else
4169
template <class>
4170
struct TC;
4171
template <class Exp, class F,
4172
          class Ret = decltype(detail::invoke(std::declval<F>(),
4173
                                              *std::declval<Exp>())),
4174
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr>
4175
auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4176
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4177
4178
  return exp.has_value()
4179
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4180
             : Ret(unexpect, std::forward<Exp>(exp).error());
4181
}
4182
4183
template <class Exp, class F,
4184
          class Ret = decltype(detail::invoke(std::declval<F>())),
4185
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr>
4186
constexpr auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4187
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4188
4189
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4190
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4191
}
4192
#endif
4193
4194
#ifdef TL_EXPECTED_CXX14
4195
template <class Exp, class F,
4196
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4197
          class Ret = decltype(detail::invoke(std::declval<F>(),
4198
                                              *std::declval<Exp>())),
4199
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4200
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4201
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4202
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4203
                                                 *std::forward<Exp>(exp)))
4204
                         : result(unexpect, std::forward<Exp>(exp).error());
4205
}
4206
4207
template <class Exp, class F,
4208
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4209
          class Ret = decltype(detail::invoke(std::declval<F>(),
4210
                                              *std::declval<Exp>())),
4211
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4212
auto expected_map_impl(Exp&& exp, F&& f) {
4213
  using result = expected<void, err_t<Exp>>;
4214
  if (exp.has_value()) {
4215
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4216
    return result();
4217
  }
4218
4219
  return result(unexpect, std::forward<Exp>(exp).error());
4220
}
4221
4222
template <class Exp, class F,
4223
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4224
          class Ret = decltype(detail::invoke(std::declval<F>())),
4225
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4226
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4227
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4228
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4229
                         : result(unexpect, std::forward<Exp>(exp).error());
4230
}
4231
4232
template <class Exp, class F,
4233
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4234
          class Ret = decltype(detail::invoke(std::declval<F>())),
4235
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4236
auto expected_map_impl(Exp&& exp, F&& f) {
4237
  using result = expected<void, err_t<Exp>>;
4238
  if (exp.has_value()) {
4239
    detail::invoke(std::forward<F>(f));
4240
    return result();
4241
  }
4242
4243
  return result(unexpect, std::forward<Exp>(exp).error());
4244
}
4245
#else
4246
template <class Exp, class F,
4247
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4248
          class Ret = decltype(detail::invoke(std::declval<F>(),
4249
                                              *std::declval<Exp>())),
4250
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4251
4252
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4253
    -> ret_t<Exp, detail::decay_t<Ret>> {
4254
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4255
4256
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4257
                                                 *std::forward<Exp>(exp)))
4258
                         : result(unexpect, std::forward<Exp>(exp).error());
4259
}
4260
4261
template <class Exp, class F,
4262
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4263
          class Ret = decltype(detail::invoke(std::declval<F>(),
4264
                                              *std::declval<Exp>())),
4265
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4266
4267
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4268
  if (exp.has_value()) {
4269
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4270
    return {};
4271
  }
4272
4273
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4274
}
4275
4276
template <class Exp, class F,
4277
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4278
          class Ret = decltype(detail::invoke(std::declval<F>())),
4279
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4280
4281
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4282
    -> ret_t<Exp, detail::decay_t<Ret>> {
4283
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4284
4285
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4286
                         : result(unexpect, std::forward<Exp>(exp).error());
4287
}
4288
4289
template <class Exp, class F,
4290
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4291
          class Ret = decltype(detail::invoke(std::declval<F>())),
4292
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4293
4294
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4295
  if (exp.has_value()) {
4296
    detail::invoke(std::forward<F>(f));
4297
    return {};
4298
  }
4299
4300
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4301
}
4302
#endif
4303
4304
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4305
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4306
template <class Exp, class F,
4307
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4308
          class Ret = decltype(detail::invoke(std::declval<F>(),
4309
                                              std::declval<Exp>().error())),
4310
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4311
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4312
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4313
  return exp.has_value()
4314
             ? result(*std::forward<Exp>(exp))
4315
             : result(unexpect, detail::invoke(std::forward<F>(f),
4316
                                               std::forward<Exp>(exp).error()));
4317
}
4318
template <class Exp, class F,
4319
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4320
          class Ret = decltype(detail::invoke(std::declval<F>(),
4321
                                              std::declval<Exp>().error())),
4322
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4323
auto map_error_impl(Exp&& exp, F&& f) {
4324
  using result = expected<exp_t<Exp>, monostate>;
4325
  if (exp.has_value()) {
4326
    return result(*std::forward<Exp>(exp));
4327
  }
4328
4329
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4330
  return result(unexpect, monostate{});
4331
}
4332
template <class Exp, class F,
4333
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4334
          class Ret = decltype(detail::invoke(std::declval<F>(),
4335
                                              std::declval<Exp>().error())),
4336
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4337
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4338
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4339
  return exp.has_value()
4340
             ? result()
4341
             : result(unexpect, detail::invoke(std::forward<F>(f),
4342
                                               std::forward<Exp>(exp).error()));
4343
}
4344
template <class Exp, class F,
4345
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4346
          class Ret = decltype(detail::invoke(std::declval<F>(),
4347
                                              std::declval<Exp>().error())),
4348
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4349
auto map_error_impl(Exp&& exp, F&& f) {
4350
  using result = expected<exp_t<Exp>, monostate>;
4351
  if (exp.has_value()) {
4352
    return result();
4353
  }
4354
4355
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4356
  return result(unexpect, monostate{});
4357
}
4358
#else
4359
template <class Exp, class F,
4360
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4361
          class Ret = decltype(detail::invoke(std::declval<F>(),
4362
                                              std::declval<Exp>().error())),
4363
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4364
constexpr auto map_error_impl(Exp&& exp, F&& f)
4365
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4366
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4367
4368
  return exp.has_value()
4369
             ? result(*std::forward<Exp>(exp))
4370
             : result(unexpect, detail::invoke(std::forward<F>(f),
4371
                                               std::forward<Exp>(exp).error()));
4372
}
4373
4374
template <class Exp, class F,
4375
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4376
          class Ret = decltype(detail::invoke(std::declval<F>(),
4377
                                              std::declval<Exp>().error())),
4378
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4379
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4380
  using result = expected<exp_t<Exp>, monostate>;
4381
  if (exp.has_value()) {
4382
    return result(*std::forward<Exp>(exp));
4383
  }
4384
4385
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4386
  return result(unexpect, monostate{});
4387
}
4388
4389
template <class Exp, class F,
4390
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4391
          class Ret = decltype(detail::invoke(std::declval<F>(),
4392
                                              std::declval<Exp>().error())),
4393
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4394
constexpr auto map_error_impl(Exp&& exp, F&& f)
4395
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4396
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4397
4398
  return exp.has_value()
4399
             ? result()
4400
             : result(unexpect, detail::invoke(std::forward<F>(f),
4401
                                               std::forward<Exp>(exp).error()));
4402
}
4403
4404
template <class Exp, class F,
4405
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4406
          class Ret = decltype(detail::invoke(std::declval<F>(),
4407
                                              std::declval<Exp>().error())),
4408
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4409
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4410
  using result = expected<exp_t<Exp>, monostate>;
4411
  if (exp.has_value()) {
4412
    return result();
4413
  }
4414
4415
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4416
  return result(unexpect, monostate{});
4417
}
4418
#endif
4419
4420
#ifdef TL_EXPECTED_CXX14
4421
template <class Exp, class F,
4422
          class Ret = decltype(detail::invoke(std::declval<F>(),
4423
                                              std::declval<Exp>().error())),
4424
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4425
constexpr auto or_else_impl(Exp&& exp, F&& f) {
4426
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4427
  return exp.has_value() ? std::forward<Exp>(exp)
4428
                         : detail::invoke(std::forward<F>(f),
4429
                                          std::forward<Exp>(exp).error());
4430
}
4431
4432
template <class Exp, class F,
4433
          class Ret = decltype(detail::invoke(std::declval<F>(),
4434
                                              std::declval<Exp>().error())),
4435
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4436
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4437
  return exp.has_value() ? std::forward<Exp>(exp)
4438
                         : (detail::invoke(std::forward<F>(f),
4439
                                           std::forward<Exp>(exp).error()),
4440
                            std::forward<Exp>(exp));
4441
}
4442
#else
4443
template <class Exp, class F,
4444
          class Ret = decltype(detail::invoke(std::declval<F>(),
4445
                                              std::declval<Exp>().error())),
4446
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4447
auto or_else_impl(Exp&& exp, F&& f) -> Ret {
4448
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4449
  return exp.has_value() ? std::forward<Exp>(exp)
4450
                         : detail::invoke(std::forward<F>(f),
4451
                                          std::forward<Exp>(exp).error());
4452
}
4453
4454
template <class Exp, class F,
4455
          class Ret = decltype(detail::invoke(std::declval<F>(),
4456
                                              std::declval<Exp>().error())),
4457
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4458
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4459
  return exp.has_value() ? std::forward<Exp>(exp)
4460
                         : (detail::invoke(std::forward<F>(f),
4461
                                           std::forward<Exp>(exp).error()),
4462
                            std::forward<Exp>(exp));
4463
}
4464
#endif
4465
}  // namespace detail
4466
4467
template <class T, class E, class U, class F>
4468
constexpr bool operator==(const expected<T, E>& lhs,
4469
                          const expected<U, F>& rhs) {
4470
  return (lhs.has_value() != rhs.has_value())
4471
             ? false
4472
             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
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
             ? true
4479
             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4480
}
4481
template <class E, class F>
4482
constexpr bool operator==(const expected<void, E>& lhs,
4483
                          const expected<void, F>& rhs) {
4484
  return (lhs.has_value() != rhs.has_value())
4485
             ? false
4486
             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
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
             ? true
4493
             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4494
}
4495
4496
template <class T, class E, class U>
4497
constexpr bool operator==(const expected<T, E>& x, const U& v) {
4498
  return x.has_value() ? *x == v : false;
4499
}
4500
template <class T, class E, class U>
4501
constexpr bool operator==(const U& v, const expected<T, E>& x) {
4502
  return x.has_value() ? *x == v : false;
4503
}
4504
template <class T, class E, class U>
4505
constexpr bool operator!=(const expected<T, E>& x, const U& v) {
4506
  return x.has_value() ? *x != v : true;
4507
}
4508
template <class T, class E, class U>
4509
constexpr bool operator!=(const U& v, const expected<T, E>& x) {
4510
  return x.has_value() ? *x != v : true;
4511
}
4512
4513
template <class T, class E>
4514
constexpr bool operator==(const expected<T, E>& x, const unexpected<E>& e) {
4515
  return x.has_value() ? false : x.error() == e.value();
4516
}
4517
template <class T, class E>
4518
constexpr bool operator==(const unexpected<E>& e, const expected<T, E>& x) {
4519
  return x.has_value() ? false : x.error() == e.value();
4520
}
4521
template <class T, class E>
4522
constexpr bool operator!=(const expected<T, E>& x, const unexpected<E>& e) {
4523
  return x.has_value() ? true : x.error() != e.value();
4524
}
4525
template <class T, class E>
4526
constexpr bool operator!=(const unexpected<E>& e, const expected<T, E>& x) {
4527
  return x.has_value() ? true : x.error() != e.value();
4528
}
4529
4530
template <class T, class E,
4531
          detail::enable_if_t<(std::is_void<T>::value ||
4532
                               std::is_move_constructible<T>::value) &&
4533
                              detail::is_swappable<T>::value &&
4534
                              std::is_move_constructible<E>::value &&
4535
                              detail::is_swappable<E>::value>* = nullptr>
4536
void swap(expected<T, E>& lhs,
4537
          expected<T, E>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
4538
  lhs.swap(rhs);
4539
}
4540
}  // namespace tl
4541
4542
#endif
4543
/* end file include/ada/expected.h */
4544
4545
/* begin file include/ada/url_pattern_regex.h */
4546
/**
4547
 * @file url_search_params.h
4548
 * @brief Declaration for the URL Search Params
4549
 */
4550
#ifndef ADA_URL_PATTERN_REGEX_H
4551
#define ADA_URL_PATTERN_REGEX_H
4552
4553
#include <string>
4554
#include <string_view>
4555
4556
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4557
#include <regex>
4558
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4559
4560
#if ADA_INCLUDE_URL_PATTERN
4561
namespace ada::url_pattern_regex {
4562
4563
template <typename T>
4564
concept regex_concept = requires(T t, std::string_view pattern,
4565
                                 bool ignore_case, std::string_view input) {
4566
  // Ensure the class has a type alias 'regex_type'
4567
  typename T::regex_type;
4568
4569
  // Function to create a regex instance
4570
  {
4571
    T::create_instance(pattern, ignore_case)
4572
  } -> std::same_as<std::optional<typename T::regex_type>>;
4573
4574
  // Function to perform regex search
4575
  {
4576
    T::regex_search(input, std::declval<typename T::regex_type&>())
4577
  } -> std::same_as<std::optional<std::vector<std::optional<std::string>>>>;
4578
4579
  // Function to match regex pattern
4580
  {
4581
    T::regex_match(input, std::declval<typename T::regex_type&>())
4582
  } -> std::same_as<bool>;
4583
4584
  // Copy constructor
4585
  { T(std::declval<const T&>()) } -> std::same_as<T>;
4586
4587
  // Move constructor
4588
  { T(std::declval<T&&>()) } -> std::same_as<T>;
4589
};
4590
4591
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4592
class std_regex_provider final {
4593
 public:
4594
  std_regex_provider() = default;
4595
  using regex_type = std::regex;
4596
  static std::optional<regex_type> create_instance(std::string_view pattern,
4597
                                                   bool ignore_case);
4598
  static std::optional<std::vector<std::optional<std::string>>> regex_search(
4599
      std::string_view input, const regex_type& pattern);
4600
  static bool regex_match(std::string_view input, const regex_type& pattern);
4601
};
4602
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4603
4604
}  // namespace ada::url_pattern_regex
4605
#endif  // ADA_INCLUDE_URL_PATTERN
4606
#endif  // ADA_URL_PATTERN_REGEX_H
4607
/* end file include/ada/url_pattern_regex.h */
4608
/* begin file include/ada/url_pattern_init.h */
4609
/**
4610
 * @file url_pattern_init.h
4611
 * @brief Declaration for the url_pattern_init implementation.
4612
 */
4613
#ifndef ADA_URL_PATTERN_INIT_H
4614
#define ADA_URL_PATTERN_INIT_H
4615
4616
/* begin file include/ada/errors.h */
4617
/**
4618
 * @file errors.h
4619
 * @brief Error type definitions for URL parsing.
4620
 *
4621
 * Defines the error codes that can be returned when URL parsing fails.
4622
 */
4623
#ifndef ADA_ERRORS_H
4624
#define ADA_ERRORS_H
4625
4626
#include <cstdint>
4627
namespace ada {
4628
/**
4629
 * @brief Error codes for URL parsing operations.
4630
 *
4631
 * Used with `tl::expected` to indicate why a URL parsing operation failed.
4632
 */
4633
enum class errors : uint8_t {
4634
  type_error /**< A type error occurred (e.g., invalid URL syntax). */
4635
};
4636
}  // namespace ada
4637
#endif  // ADA_ERRORS_H
4638
/* end file include/ada/errors.h */
4639
4640
#include <string_view>
4641
#include <string>
4642
#include <optional>
4643
#include <iostream>
4644
4645
#if ADA_TESTING
4646
#include <iostream>
4647
#endif  // ADA_TESTING
4648
4649
#if ADA_INCLUDE_URL_PATTERN
4650
namespace ada {
4651
4652
// Important: C++20 allows us to use concept rather than `using` or `typedef
4653
// and allows functions with second argument, which is optional (using either
4654
// std::nullopt or a parameter with default value)
4655
template <typename F>
4656
concept url_pattern_encoding_callback = requires(F f, std::string_view sv) {
4657
  { f(sv) } -> std::same_as<tl::expected<std::string, errors>>;
4658
};
4659
4660
// A structure providing matching patterns for individual components
4661
// of a URL. When a URLPattern is created, or when a URLPattern is
4662
// used to match or test against a URL, the input can be given as
4663
// either a string or a URLPatternInit struct. If a string is given,
4664
// it will be parsed to create a URLPatternInit. The URLPatternInit
4665
// API is defined as part of the URLPattern specification.
4666
// All provided strings must be valid UTF-8.
4667
struct url_pattern_init {
4668
  enum class process_type : uint8_t {
4669
    url,
4670
    pattern,
4671
  };
4672
4673
0
  friend std::ostream& operator<<(std::ostream& os, process_type type) {
4674
0
    switch (type) {
4675
0
      case process_type::url:
4676
0
        return os << "url";
4677
0
      case process_type::pattern:
4678
0
        return os << "pattern";
4679
0
      default:
4680
0
        return os << "unknown";
4681
0
    }
4682
0
  }
4683
4684
  // All strings must be valid UTF-8.
4685
  // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
4686
  static tl::expected<url_pattern_init, errors> process(
4687
      const url_pattern_init& init, process_type type,
4688
      std::optional<std::string_view> protocol = std::nullopt,
4689
      std::optional<std::string_view> username = std::nullopt,
4690
      std::optional<std::string_view> password = std::nullopt,
4691
      std::optional<std::string_view> hostname = std::nullopt,
4692
      std::optional<std::string_view> port = std::nullopt,
4693
      std::optional<std::string_view> pathname = std::nullopt,
4694
      std::optional<std::string_view> search = std::nullopt,
4695
      std::optional<std::string_view> hash = std::nullopt);
4696
4697
  // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init
4698
  static tl::expected<std::string, errors> process_protocol(
4699
      std::string_view value, process_type type);
4700
4701
  // @see https://urlpattern.spec.whatwg.org/#process-username-for-init
4702
  static tl::expected<std::string, errors> process_username(
4703
      std::string_view value, process_type type);
4704
4705
  // @see https://urlpattern.spec.whatwg.org/#process-password-for-init
4706
  static tl::expected<std::string, errors> process_password(
4707
      std::string_view value, process_type type);
4708
4709
  // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init
4710
  static tl::expected<std::string, errors> process_hostname(
4711
      std::string_view value, process_type type);
4712
4713
  // @see https://urlpattern.spec.whatwg.org/#process-port-for-init
4714
  static tl::expected<std::string, errors> process_port(
4715
      std::string_view port, std::string_view protocol, process_type type);
4716
4717
  // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init
4718
  static tl::expected<std::string, errors> process_pathname(
4719
      std::string_view value, std::string_view protocol, process_type type);
4720
4721
  // @see https://urlpattern.spec.whatwg.org/#process-search-for-init
4722
  static tl::expected<std::string, errors> process_search(
4723
      std::string_view value, process_type type);
4724
4725
  // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init
4726
  static tl::expected<std::string, errors> process_hash(std::string_view value,
4727
                                                        process_type type);
4728
4729
#if ADA_TESTING
4730
  friend void PrintTo(const url_pattern_init& init, std::ostream* os) {
4731
    *os << "protocol: '" << init.protocol.value_or("undefined") << "', ";
4732
    *os << "username: '" << init.username.value_or("undefined") << "', ";
4733
    *os << "password: '" << init.password.value_or("undefined") << "', ";
4734
    *os << "hostname: '" << init.hostname.value_or("undefined") << "', ";
4735
    *os << "port: '" << init.port.value_or("undefined") << "', ";
4736
    *os << "pathname: '" << init.pathname.value_or("undefined") << "', ";
4737
    *os << "search: '" << init.search.value_or("undefined") << "', ";
4738
    *os << "hash: '" << init.hash.value_or("undefined") << "', ";
4739
    *os << "base_url: '" << init.base_url.value_or("undefined") << "', ";
4740
  }
4741
#endif  // ADA_TESTING
4742
4743
  bool operator==(const url_pattern_init&) const;
4744
  // If present, must be valid UTF-8.
4745
  std::optional<std::string> protocol{};
4746
  // If present, must be valid UTF-8.
4747
  std::optional<std::string> username{};
4748
  // If present, must be valid UTF-8.
4749
  std::optional<std::string> password{};
4750
  // If present, must be valid UTF-8.
4751
  std::optional<std::string> hostname{};
4752
  // If present, must be valid UTF-8.
4753
  std::optional<std::string> port{};
4754
  // If present, must be valid UTF-8.
4755
  std::optional<std::string> pathname{};
4756
  // If present, must be valid UTF-8.
4757
  std::optional<std::string> search{};
4758
  // If present, must be valid UTF-8.
4759
  std::optional<std::string> hash{};
4760
  // If present, must be valid UTF-8.
4761
  std::optional<std::string> base_url{};
4762
};
4763
}  // namespace ada
4764
#endif  // ADA_INCLUDE_URL_PATTERN
4765
#endif  // ADA_URL_PATTERN_INIT_H
4766
/* end file include/ada/url_pattern_init.h */
4767
4768
/** @private Forward declarations */
4769
namespace ada {
4770
struct url_aggregator;
4771
struct url;
4772
#if ADA_INCLUDE_URL_PATTERN
4773
template <url_pattern_regex::regex_concept regex_provider>
4774
class url_pattern;
4775
struct url_pattern_options;
4776
#endif  // ADA_INCLUDE_URL_PATTERN
4777
enum class errors : uint8_t;
4778
}  // namespace ada
4779
4780
/**
4781
 * @namespace ada::parser
4782
 * @brief Internal URL parsing implementation.
4783
 *
4784
 * Contains the core URL parsing algorithm as specified by the WHATWG URL
4785
 * Standard. These functions are used internally by `ada::parse()`.
4786
 */
4787
namespace ada::parser {
4788
/**
4789
 * Parses a URL string into a URL object.
4790
 *
4791
 * @tparam result_type The type of URL object to create (url or url_aggregator).
4792
 *
4793
 * @param user_input The URL string to parse (must be valid UTF-8).
4794
 * @param base_url Optional base URL for resolving relative URLs.
4795
 *
4796
 * @return The parsed URL object. Check `is_valid` to determine if parsing
4797
 *         succeeded.
4798
 *
4799
 * @see https://url.spec.whatwg.org/#concept-basic-url-parser
4800
 */
4801
template <typename result_type = url_aggregator>
4802
result_type parse_url(std::string_view user_input,
4803
                      const result_type* base_url = nullptr);
4804
4805
extern template url_aggregator parse_url<url_aggregator>(
4806
    std::string_view user_input, const url_aggregator* base_url);
4807
extern template url parse_url<url>(std::string_view user_input,
4808
                                   const url* base_url);
4809
4810
template <typename result_type = url_aggregator, bool store_values = true>
4811
result_type parse_url_impl(std::string_view user_input,
4812
                           const result_type* base_url = nullptr);
4813
4814
extern template url_aggregator parse_url_impl<url_aggregator, true>(
4815
    std::string_view user_input, const url_aggregator* base_url);
4816
extern template url_aggregator parse_url_impl<url_aggregator, false>(
4817
    std::string_view user_input, const url_aggregator* base_url);
4818
extern template url parse_url_impl<url, true>(std::string_view user_input,
4819
                                              const url* base_url);
4820
4821
/** @private */
4822
template <class result_type>
4823
bool try_parse_simple_absolute(std::string_view input, result_type& out);
4824
4825
/** @private */
4826
template <class result_type>
4827
bool finish_simple_absolute_with_port(std::string_view input, result_type& out,
4828
                                      ada::scheme::type scheme_type,
4829
                                      uint32_t protocol_end, size_t host_start,
4830
                                      size_t host_end, size_t host_len,
4831
                                      bool has_upper);
4832
4833
/** @private */
4834
template <class result_type>
4835
bool try_parse_simple_relative(std::string_view input, const result_type& base,
4836
                               result_type& out);
4837
4838
#if ADA_INCLUDE_URL_PATTERN
4839
template <url_pattern_regex::regex_concept regex_provider>
4840
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
4841
    std::variant<std::string_view, url_pattern_init>&& input,
4842
    const std::string_view* base_url, const url_pattern_options* options);
4843
#endif  // ADA_INCLUDE_URL_PATTERN
4844
4845
}  // namespace ada::parser
4846
4847
#endif  // ADA_PARSER_H
4848
/* end file include/ada/parser.h */
4849
/* begin file include/ada/parser-inl.h */
4850
/**
4851
 * @file parser-inl.h
4852
 */
4853
#ifndef ADA_PARSER_INL_H
4854
#define ADA_PARSER_INL_H
4855
4856
/* begin file include/ada/url_pattern.h */
4857
/**
4858
 * @file url_pattern.h
4859
 * @brief URLPattern API implementation.
4860
 *
4861
 * This header provides the URLPattern API as specified by the WHATWG URL
4862
 * Pattern Standard. URLPattern allows matching URLs against patterns with
4863
 * wildcards and named groups, similar to how regular expressions match strings.
4864
 *
4865
 * @see https://urlpattern.spec.whatwg.org/
4866
 * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
4867
 */
4868
#ifndef ADA_URL_PATTERN_H
4869
#define ADA_URL_PATTERN_H
4870
4871
/* begin file include/ada/implementation.h */
4872
/**
4873
 * @file implementation.h
4874
 * @brief User-facing functions for URL parsing and manipulation.
4875
 *
4876
 * This header provides the primary public API for parsing URLs in Ada.
4877
 * It includes the main `ada::parse()` function which is the recommended
4878
 * entry point for most users.
4879
 *
4880
 * @see https://url.spec.whatwg.org/#api
4881
 */
4882
#ifndef ADA_IMPLEMENTATION_H
4883
#define ADA_IMPLEMENTATION_H
4884
4885
#include <string>
4886
#include <string_view>
4887
#include <optional>
4888
4889
/* begin file include/ada/url.h */
4890
/**
4891
 * @file url.h
4892
 * @brief Declaration for the `ada::url` class.
4893
 *
4894
 * This file contains the `ada::url` struct which represents a parsed URL
4895
 * using separate `std::string` instances for each component. This
4896
 * representation is more flexible but uses more memory than `url_aggregator`.
4897
 *
4898
 * @see url_aggregator.h for a more memory-efficient alternative
4899
 */
4900
#ifndef ADA_URL_H
4901
#define ADA_URL_H
4902
4903
#include <algorithm>
4904
#include <optional>
4905
#include <ostream>
4906
#include <string>
4907
#include <string_view>
4908
4909
/* begin file include/ada/url_components.h */
4910
/**
4911
 * @file url_components.h
4912
 * @brief URL component offset representation for url_aggregator.
4913
 *
4914
 * This file defines the `url_components` struct which stores byte offsets
4915
 * into a URL string buffer. It is used internally by `url_aggregator` to
4916
 * efficiently locate URL components without storing separate strings.
4917
 */
4918
#ifndef ADA_URL_COMPONENTS_H
4919
#define ADA_URL_COMPONENTS_H
4920
4921
namespace ada {
4922
4923
/**
4924
 * @brief Stores byte offsets for URL components within a buffer.
4925
 *
4926
 * The `url_components` struct uses 32-bit offsets to track the boundaries
4927
 * of each URL component within a single string buffer. This enables efficient
4928
 * component extraction without additional memory allocations.
4929
 *
4930
 * Component layout in a URL:
4931
 * ```
4932
 * https://user:pass@example.com:1234/foo/bar?baz#quux
4933
 *       |     |    |          | ^^^^|       |   |
4934
 *       |     |    |          | |   |       |   `----- hash_start
4935
 *       |     |    |          | |   |       `--------- search_start
4936
 *       |     |    |          | |   `----------------- pathname_start
4937
 *       |     |    |          | `--------------------- port
4938
 *       |     |    |          `----------------------- host_end
4939
 *       |     |    `---------------------------------- host_start
4940
 *       |     `--------------------------------------- username_end
4941
 *       `--------------------------------------------- protocol_end
4942
 * ```
4943
 *
4944
 * @note The 32-bit offsets limit URLs to 4GB in length.
4945
 * @note A value of `omitted` (UINT32_MAX) indicates the component is not
4946
 * present.
4947
 */
4948
struct url_components {
4949
  /** Sentinel value indicating a component is not present. */
4950
  constexpr static uint32_t omitted = uint32_t(-1);
4951
4952
313k
  url_components() = default;
4953
  url_components(const url_components& u) = default;
4954
  url_components(url_components&& u) noexcept = default;
4955
  url_components& operator=(url_components&& u) noexcept = default;
4956
  url_components& operator=(const url_components& u) = default;
4957
  ~url_components() = default;
4958
4959
  /** Offset of the end of the protocol/scheme (position of ':'). */
4960
  uint32_t protocol_end{0};
4961
4962
  /**
4963
   * Offset of the end of the username.
4964
   * Initialized to 0 (not `omitted`) to simplify username/password getters.
4965
   */
4966
  uint32_t username_end{0};
4967
4968
  /** Offset of the start of the host. */
4969
  uint32_t host_start{0};
4970
4971
  /** Offset of the end of the host. */
4972
  uint32_t host_end{0};
4973
4974
  /** Port number, or `omitted` if no port is specified. */
4975
  uint32_t port{omitted};
4976
4977
  /** Offset of the start of the pathname. */
4978
  uint32_t pathname_start{0};
4979
4980
  /** Offset of the '?' starting the query, or `omitted` if no query. */
4981
  uint32_t search_start{omitted};
4982
4983
  /** Offset of the '#' starting the fragment, or `omitted` if no fragment. */
4984
  uint32_t hash_start{omitted};
4985
4986
  /**
4987
   * Validates that offsets are in ascending order and consistent.
4988
   * Useful for debugging to detect internal corruption.
4989
   * @return `true` if offsets are consistent, `false` otherwise.
4990
   */
4991
  [[nodiscard]] constexpr bool check_offset_consistency() const noexcept;
4992
4993
  /**
4994
   * Returns a JSON string representation of the offsets for debugging.
4995
   * @return A JSON-formatted string with all offset values.
4996
   */
4997
  [[nodiscard]] std::string to_string() const;
4998
4999
};  // struct url_components
5000
}  // namespace ada
5001
#endif
5002
/* end file include/ada/url_components.h */
5003
5004
namespace ada {
5005
5006
struct url_aggregator;
5007
5008
// namespace parser {
5009
// template <typename result_type>
5010
// result_type parse_url(std::string_view user_input,
5011
//                       const result_type* base_url = nullptr);
5012
// template <typename result_type, bool store_values>
5013
// result_type parse_url_impl(std::string_view user_input,
5014
//                            const result_type* base_url = nullptr);
5015
// }
5016
5017
/**
5018
 * @brief Represents a parsed URL with individual string components.
5019
 *
5020
 * The `url` struct stores each URL component (scheme, username, password,
5021
 * host, port, path, query, fragment) as a separate `std::string`. This
5022
 * provides flexibility but incurs more memory allocations compared to
5023
 * `url_aggregator`.
5024
 *
5025
 * **When to use `ada::url`:**
5026
 * - When you need to frequently modify individual URL components
5027
 * - When you want independent ownership of component strings
5028
 *
5029
 * **When to use `ada::url_aggregator` instead:**
5030
 * - For read-mostly operations on parsed URLs
5031
 * - When memory efficiency is important
5032
 * - When you only need string_view access to components
5033
 *
5034
 * @note This type is returned when parsing with `ada::parse<ada::url>()`.
5035
 *       By default, `ada::parse()` returns `ada::url_aggregator`.
5036
 *
5037
 * @see url_aggregator For a more memory-efficient URL representation
5038
 * @see https://url.spec.whatwg.org/#url-representation
5039
 */
5040
struct url : url_base {
5041
0
  url() = default;
5042
0
  url(const url& u) = default;
5043
0
  url(url&& u) noexcept = default;
5044
0
  url& operator=(url&& u) noexcept = default;
5045
0
  url& operator=(const url& u) = default;
5046
0
  ~url() override = default;
5047
5048
  // Fields are ordered so that the most frequently accessed components
5049
  // tend to occupy earlier cache lines and remain close together in memory.
5050
  //
5051
  // Note: The exact object layout (including cache-line boundaries, byte
5052
  // offsets, and member sizes) is implementation- and platform-dependent.
5053
  // This ordering expresses an intent for better cache locality but does not
5054
  // guarantee any specific in-memory layout.
5055
5056
  /**
5057
   * @private
5058
   * A URL's host is null or a host. It is initially null.
5059
   */
5060
  std::optional<std::string> host{};
5061
5062
  /**
5063
   * @private
5064
   * A URL's path is either an ASCII string or a list of zero or more ASCII
5065
   * strings, usually identifying a location.
5066
   */
5067
  std::string path{};
5068
5069
  /**
5070
   * @private
5071
   * A URL's query is either null or an ASCII string. It is initially null.
5072
   */
5073
  std::optional<std::string> query{};
5074
5075
  /**
5076
   * @private
5077
   * A URL's fragment is either null or an ASCII string that can be used for
5078
   * further processing on the resource the URL's other components identify. It
5079
   * is initially null.
5080
   */
5081
  std::optional<std::string> hash{};
5082
5083
  /**
5084
   * @private
5085
   * A URL's port is either null or a 16-bit unsigned integer that identifies a
5086
   * networking port. It is initially null.
5087
   */
5088
  std::optional<uint16_t> port{};
5089
5090
  /**
5091
   * @private
5092
   * A URL's username is an ASCII string identifying a username. It is initially
5093
   * the empty string.
5094
   */
5095
  std::string username{};
5096
5097
  /**
5098
   * @private
5099
   * A URL's password is an ASCII string identifying a password. It is initially
5100
   * the empty string.
5101
   */
5102
  std::string password{};
5103
5104
  /**
5105
   * Checks if the URL has an empty hostname (host is set but empty string).
5106
   * @return `true` if host exists but is empty, `false` otherwise.
5107
   */
5108
  [[nodiscard]] inline bool has_empty_hostname() const noexcept;
5109
5110
  /**
5111
   * Checks if the URL has a non-default port explicitly specified.
5112
   * @return `true` if a port is present, `false` otherwise.
5113
   */
5114
  [[nodiscard]] inline bool has_port() const noexcept;
5115
5116
  /**
5117
   * Checks if the URL has a hostname (including empty hostnames).
5118
   * @return `true` if host is present, `false` otherwise.
5119
   */
5120
  [[nodiscard]] inline bool has_hostname() const noexcept;
5121
5122
  /**
5123
   * Validates whether the hostname is a valid domain according to RFC 1034.
5124
   * Checks that the domain and its labels have valid lengths (max 255 octets
5125
   * total, max 63 octets per label).
5126
   * @return `true` if the domain is valid, `false` otherwise.
5127
   */
5128
  [[nodiscard]] bool has_valid_domain() const noexcept override;
5129
5130
  /**
5131
   * Returns a JSON string representation of this URL for debugging.
5132
   * @return A JSON-formatted string with all URL components.
5133
   */
5134
  [[nodiscard]] std::string to_string() const override;
5135
5136
  /**
5137
   * Returns the full serialized URL (the href).
5138
   * @return The complete URL string (allocates a new string).
5139
   * @see https://url.spec.whatwg.org/#dom-url-href
5140
   */
5141
  [[nodiscard]] ada_really_inline std::string get_href() const;
5142
5143
  /**
5144
   * Returns the byte length of the serialized URL without allocating a string.
5145
   * @return Size of the href in bytes.
5146
   */
5147
  [[nodiscard]] size_t get_href_size() const noexcept;
5148
5149
  /**
5150
   * Returns the URL's origin as a string (scheme + host + port for special
5151
   * URLs).
5152
   * @return A newly allocated string containing the serialized origin.
5153
   * @see https://url.spec.whatwg.org/#concept-url-origin
5154
   */
5155
  [[nodiscard]] std::string get_origin() const override;
5156
5157
  /**
5158
   * Returns the URL's scheme followed by a colon (e.g., "https:").
5159
   * @return A newly allocated string with the protocol.
5160
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5161
   */
5162
  [[nodiscard]] std::string get_protocol() const;
5163
5164
  /**
5165
   * Returns the URL's host and port (e.g., "example.com:8080").
5166
   * If no port is set, returns just the host. Returns empty string if no host.
5167
   * @return A newly allocated string with host:port.
5168
   * @see https://url.spec.whatwg.org/#dom-url-host
5169
   */
5170
  [[nodiscard]] std::string get_host() const;
5171
5172
  /**
5173
   * Returns the URL's hostname (without port).
5174
   * Returns empty string if no host is set.
5175
   * @return A newly allocated string with the hostname.
5176
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5177
   */
5178
  [[nodiscard]] std::string get_hostname() const;
5179
5180
  /**
5181
   * Returns the URL's path component.
5182
   * @return A string_view pointing to the path.
5183
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5184
   */
5185
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept;
5186
5187
  /**
5188
   * Returns the byte length of the pathname without creating a string.
5189
   * @return Size of the pathname in bytes.
5190
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5191
   */
5192
  [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
5193
5194
  /**
5195
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
5196
   * Returns empty string if no query is set.
5197
   * @return A newly allocated string with the search/query.
5198
   * @see https://url.spec.whatwg.org/#dom-url-search
5199
   */
5200
  [[nodiscard]] std::string get_search() const;
5201
5202
  /**
5203
   * Returns the URL's username component.
5204
   * @return A constant reference to the username string.
5205
   * @see https://url.spec.whatwg.org/#dom-url-username
5206
   */
5207
  [[nodiscard]] const std::string& get_username() const noexcept;
5208
5209
  /**
5210
   * Sets the URL's username, percent-encoding special characters.
5211
   * @param input The new username value.
5212
   * @return `true` on success, `false` if the URL cannot have credentials.
5213
   * @see https://url.spec.whatwg.org/#dom-url-username
5214
   */
5215
  bool set_username(std::string_view input);
5216
5217
  /**
5218
   * Sets the URL's password, percent-encoding special characters.
5219
   * @param input The new password value.
5220
   * @return `true` on success, `false` if the URL cannot have credentials.
5221
   * @see https://url.spec.whatwg.org/#dom-url-password
5222
   */
5223
  bool set_password(std::string_view input);
5224
5225
  /**
5226
   * Sets the URL's port from a string (e.g., "8080").
5227
   * @param input The port string. Empty string removes the port.
5228
   * @return `true` on success, `false` if the URL cannot have a port.
5229
   * @see https://url.spec.whatwg.org/#dom-url-port
5230
   */
5231
  bool set_port(std::string_view input);
5232
5233
  /**
5234
   * Sets the URL's fragment/hash (the part after '#').
5235
   * @param input The new hash value (with or without leading '#').
5236
   * @see https://url.spec.whatwg.org/#dom-url-hash
5237
   */
5238
  void set_hash(std::string_view input);
5239
5240
  /**
5241
   * Sets the URL's query string (the part after '?').
5242
   * @param input The new query value (with or without leading '?').
5243
   * @see https://url.spec.whatwg.org/#dom-url-search
5244
   */
5245
  void set_search(std::string_view input);
5246
5247
  /**
5248
   * Sets the URL's pathname.
5249
   * @param input The new path value.
5250
   * @return `true` on success, `false` if the URL has an opaque path.
5251
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5252
   */
5253
  bool set_pathname(std::string_view input);
5254
5255
  /**
5256
   * Sets the URL's host (hostname and optionally port).
5257
   * @param input The new host value (e.g., "example.com:8080").
5258
   * @return `true` on success, `false` if parsing fails.
5259
   * @see https://url.spec.whatwg.org/#dom-url-host
5260
   */
5261
  bool set_host(std::string_view input);
5262
5263
  /**
5264
   * Sets the URL's hostname (without port).
5265
   * @param input The new hostname value.
5266
   * @return `true` on success, `false` if parsing fails.
5267
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5268
   */
5269
  bool set_hostname(std::string_view input);
5270
5271
  /**
5272
   * Sets the URL's protocol/scheme.
5273
   * @param input The new protocol (with or without trailing ':').
5274
   * @return `true` on success, `false` if the scheme is invalid.
5275
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5276
   */
5277
  bool set_protocol(std::string_view input);
5278
5279
  /**
5280
   * Replaces the entire URL by parsing a new href string.
5281
   * @param input The new URL string to parse.
5282
   * @return `true` on success, `false` if parsing fails.
5283
   * @see https://url.spec.whatwg.org/#dom-url-href
5284
   */
5285
  bool set_href(std::string_view input);
5286
5287
  /**
5288
   * Returns the URL's password component.
5289
   * @return A constant reference to the password string.
5290
   * @see https://url.spec.whatwg.org/#dom-url-password
5291
   */
5292
  [[nodiscard]] const std::string& get_password() const noexcept;
5293
5294
  /**
5295
   * Returns the URL's port as a string (e.g., "8080").
5296
   * Returns empty string if no port is set.
5297
   * @return A newly allocated string with the port.
5298
   * @see https://url.spec.whatwg.org/#dom-url-port
5299
   */
5300
  [[nodiscard]] std::string get_port() const;
5301
5302
  /**
5303
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
5304
   * Returns empty string if no fragment is set.
5305
   * @return A newly allocated string with the hash.
5306
   * @see https://url.spec.whatwg.org/#dom-url-hash
5307
   */
5308
  [[nodiscard]] std::string get_hash() const;
5309
5310
  /**
5311
   * Checks if the URL has credentials (non-empty username or password).
5312
   * @return `true` if username or password is non-empty, `false` otherwise.
5313
   */
5314
  [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5315
5316
  /**
5317
   * Returns the URL component offsets for efficient serialization.
5318
   *
5319
   * The components represent byte offsets into the serialized URL:
5320
   * ```
5321
   * https://user:pass@example.com:1234/foo/bar?baz#quux
5322
   *       |     |    |          | ^^^^|       |   |
5323
   *       |     |    |          | |   |       |   `----- hash_start
5324
   *       |     |    |          | |   |       `--------- search_start
5325
   *       |     |    |          | |   `----------------- pathname_start
5326
   *       |     |    |          | `--------------------- port
5327
   *       |     |    |          `----------------------- host_end
5328
   *       |     |    `---------------------------------- host_start
5329
   *       |     `--------------------------------------- username_end
5330
   *       `--------------------------------------------- protocol_end
5331
   * ```
5332
   * @return A newly constructed url_components struct.
5333
   * @see https://github.com/servo/rust-url
5334
   */
5335
  [[nodiscard]] ada_really_inline ada::url_components get_components() const;
5336
5337
  /**
5338
   * Checks if the URL has a fragment/hash component.
5339
   * @return `true` if hash is present, `false` otherwise.
5340
   */
5341
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
5342
5343
  /**
5344
   * Checks if the URL has a query/search component.
5345
   * @return `true` if query is present, `false` otherwise.
5346
   */
5347
  [[nodiscard]] constexpr bool has_search() const noexcept override;
5348
5349
 private:
5350
  friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5351
                                                   const ada::url*);
5352
  friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5353
      std::string_view, const ada::url_aggregator*);
5354
  friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5355
      ada::url& url);
5356
5357
  friend ada::url ada::parser::parse_url_impl<ada::url, true>(std::string_view,
5358
                                                              const ada::url*);
5359
  friend ada::url_aggregator ada::parser::parse_url_impl<
5360
      ada::url_aggregator, true>(std::string_view, const ada::url_aggregator*);
5361
  friend ada::url_aggregator ada::parser::parse_url_impl<
5362
      ada::url_aggregator, false>(std::string_view, const ada::url_aggregator*);
5363
  template <class result_type>
5364
  friend bool ada::parser::try_parse_simple_absolute(std::string_view,
5365
                                                     result_type&);
5366
  template <class result_type>
5367
  friend bool ada::parser::finish_simple_absolute_with_port(
5368
      std::string_view, result_type&, ada::scheme::type, uint32_t, size_t,
5369
      size_t, size_t, bool);
5370
  template <class result_type>
5371
  friend bool ada::parser::try_parse_simple_relative(std::string_view,
5372
                                                     const result_type&,
5373
                                                     result_type&);
5374
5375
  inline void update_unencoded_base_hash(std::string_view input);
5376
  inline void update_base_hostname(std::string_view input);
5377
  inline void update_base_search(std::string_view input,
5378
                                 const uint8_t query_percent_encode_set[]);
5379
  inline void update_base_search(std::optional<std::string>&& input);
5380
  inline void update_base_pathname(std::string_view input);
5381
  inline void update_base_username(std::string_view input);
5382
  inline void update_base_password(std::string_view input);
5383
  inline void update_base_port(std::optional<uint16_t> input);
5384
5385
  /**
5386
   * Returns true if growing href by input_len (worst case) could exceed
5387
   * get_max_input_length(), meaning a rollback snapshot is actually needed.
5388
   */
5389
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
5390
5391
  /**
5392
   * Sets the host or hostname according to override condition.
5393
   * Return true on success.
5394
   * @see https://url.spec.whatwg.org/#hostname-state
5395
   */
5396
  template <bool override_hostname = false>
5397
  bool set_host_or_hostname(std::string_view input);
5398
5399
  /**
5400
   * Return true on success.
5401
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5402
   */
5403
  [[nodiscard]] bool parse_ipv4(std::string_view input);
5404
5405
  /**
5406
   * Return true on success.
5407
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5408
   */
5409
  [[nodiscard]] bool parse_ipv6(std::string_view input);
5410
5411
  /**
5412
   * Return true on success.
5413
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5414
   */
5415
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
5416
5417
  /**
5418
   * A URL's scheme is an ASCII string that identifies the type of URL and can
5419
   * be used to dispatch a URL for further processing after parsing. It is
5420
   * initially the empty string. We only set non_special_scheme when the scheme
5421
   * is non-special, otherwise we avoid constructing string.
5422
   *
5423
   * Special schemes are stored in ada::scheme::details::is_special_list so we
5424
   * typically do not need to store them in each url instance.
5425
   */
5426
  std::string non_special_scheme{};
5427
5428
  /**
5429
   * A URL cannot have a username/password/port if its host is null or the empty
5430
   * string, or its scheme is "file".
5431
   */
5432
  [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5433
5434
  ada_really_inline size_t parse_port(
5435
      std::string_view view, bool check_trailing_content) noexcept override;
5436
5437
0
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5438
0
    return this->parse_port(view, false);
5439
0
  }
5440
5441
  /**
5442
   * Parse the host from the provided input. We assume that
5443
   * the input does not contain spaces or tabs. Control
5444
   * characters and spaces are not trimmed (they should have
5445
   * been removed if needed).
5446
   * Return true on success.
5447
   * @see https://url.spec.whatwg.org/#host-parsing
5448
   */
5449
  [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5450
5451
  template <bool has_state_override = false>
5452
  [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5453
5454
  constexpr void clear_pathname() override;
5455
  constexpr void clear_search() override;
5456
  constexpr void set_protocol_as_file();
5457
5458
  /**
5459
   * Parse the path from the provided input.
5460
   * Return true on success. Control characters not
5461
   * trimmed from the ends (they should have
5462
   * been removed if needed).
5463
   *
5464
   * The input is expected to be UTF-8.
5465
   *
5466
   * @see https://url.spec.whatwg.org/
5467
   */
5468
  ada_really_inline void parse_path(std::string_view input);
5469
5470
  /**
5471
   * Set the scheme for this URL. The provided scheme should be a valid
5472
   * scheme string, be lower-cased, not contain spaces or tabs. It should
5473
   * have no spurious trailing or leading content.
5474
   */
5475
  inline void set_scheme(std::string&& new_scheme) noexcept;
5476
5477
  /**
5478
   * Take the scheme from another URL. The scheme string is moved from the
5479
   * provided url.
5480
   */
5481
  constexpr void copy_scheme(ada::url&& u);
5482
5483
  /**
5484
   * Take the scheme from another URL. The scheme string is copied from the
5485
   * provided url.
5486
   */
5487
  constexpr void copy_scheme(const ada::url& u);
5488
5489
};  // struct url
5490
5491
inline std::ostream& operator<<(std::ostream& out, const ada::url& u);
5492
}  // namespace ada
5493
5494
#endif  // ADA_URL_H
5495
/* end file include/ada/url.h */
5496
5497
namespace ada {
5498
5499
/**
5500
 * Result type for URL parsing operations.
5501
 *
5502
 * Uses `tl::expected` to represent either a successfully parsed URL or an
5503
 * error. This allows for exception-free error handling.
5504
 *
5505
 * @tparam result_type The URL type to return (default: `ada::url_aggregator`)
5506
 *
5507
 * @example
5508
 * ```cpp
5509
 * ada::result<ada::url_aggregator> result = ada::parse("https://example.com");
5510
 * if (result) {
5511
 *     // Success: use result.value() or *result
5512
 * } else {
5513
 *     // Error: handle result.error()
5514
 * }
5515
 * ```
5516
 */
5517
template <class result_type = ada::url_aggregator>
5518
using result = tl::expected<result_type, ada::errors>;
5519
5520
/**
5521
 * Parses a URL string according to the WHATWG URL Standard.
5522
 *
5523
 * This is the main entry point for URL parsing in Ada. The function takes
5524
 * a string input and optionally a base URL for resolving relative URLs.
5525
 *
5526
 * @tparam result_type The URL type to return. Can be either `ada::url` or
5527
 *         `ada::url_aggregator` (default). The `url_aggregator` type is more
5528
 *         memory-efficient as it stores components as offsets into a single
5529
 *         buffer.
5530
 *
5531
 * @param input The URL string to parse. Must be valid ASCII or UTF-8 encoded.
5532
 *        Leading and trailing whitespace is automatically trimmed.
5533
 * @param base_url Optional pointer to a base URL for resolving relative URLs.
5534
 *        If nullptr (default), only absolute URLs can be parsed successfully.
5535
 *
5536
 * @return A `result<result_type>` containing either the parsed URL on success,
5537
 *         or an error code on failure. Use the boolean conversion or
5538
 *         `has_value()` to check for success.
5539
 *
5540
 * @note The parser is fully compliant with the WHATWG URL Standard.
5541
 *
5542
 * Parsing fails if the input or the resulting normalized URL exceeds
5543
 * `get_max_input_length()` bytes (default ~4 GB, configurable via
5544
 * `set_max_input_length()`). This accounts for percent-encoding expansion:
5545
 * a short input that normalizes into a long URL is still rejected.
5546
 *
5547
 * @example
5548
 * ```cpp
5549
 * // Parse an absolute URL
5550
 * auto url = ada::parse("https://user:pass@example.com:8080/path?query#hash");
5551
 * if (url) {
5552
 *     std::cout << url->get_hostname(); // "example.com"
5553
 *     std::cout << url->get_pathname(); // "/path"
5554
 * }
5555
 *
5556
 * // Parse a relative URL with a base
5557
 * auto base = ada::parse("https://example.com/dir/");
5558
 * if (base) {
5559
 *     auto relative = ada::parse("../other/page", &*base);
5560
 *     if (relative) {
5561
 *         std::cout << relative->get_href(); //
5562
 * "https://example.com/other/page"
5563
 *     }
5564
 * }
5565
 * ```
5566
 *
5567
 * @see https://url.spec.whatwg.org/#url-parsing
5568
 */
5569
template <class result_type = ada::url_aggregator>
5570
ada_warn_unused ada::result<result_type> parse(
5571
    std::string_view input, const result_type* base_url = nullptr);
5572
5573
extern template ada::result<url> parse<url>(std::string_view input,
5574
                                            const url* base_url);
5575
extern template ada::result<url_aggregator> parse<url_aggregator>(
5576
    std::string_view input, const url_aggregator* base_url);
5577
5578
/**
5579
 * Checks whether a URL string can be successfully parsed.
5580
 *
5581
 * Equivalent to `parse(input, base).has_value()` for every input, including
5582
 * when `set_max_input_length` rejects a normalized href that exceeds the limit.
5583
 *
5584
 * @param input The URL string to validate. Must be valid ASCII or UTF-8.
5585
 * @param base_input Optional base URL string for relative inputs.
5586
 * @return `true` if parsing would succeed, `false` otherwise.
5587
 * @see https://url.spec.whatwg.org/#dom-url-canparse
5588
 */
5589
bool can_parse(std::string_view input,
5590
               const std::string_view* base_input = nullptr);
5591
5592
#if ADA_INCLUDE_URL_PATTERN
5593
/**
5594
 * Parses a URL pattern according to the URLPattern specification.
5595
 *
5596
 * URL patterns provide a syntax for matching URLs against patterns, similar
5597
 * to how regular expressions match strings. This is useful for routing and
5598
 * URL-based dispatching.
5599
 *
5600
 * @tparam regex_provider The regex implementation to use for pattern matching.
5601
 *
5602
 * @param input Either a URL pattern string (valid UTF-8) or a URLPatternInit
5603
 *        struct specifying individual component patterns.
5604
 * @param base_url Optional pointer to a base URL string (valid UTF-8) for
5605
 *        resolving relative patterns.
5606
 * @param options Optional pointer to configuration options (e.g., ignore_case).
5607
 *
5608
 * @return A `tl::expected` containing either the parsed url_pattern on success,
5609
 *         or an error code on failure.
5610
 *
5611
 * @see https://urlpattern.spec.whatwg.org
5612
 */
5613
template <url_pattern_regex::regex_concept regex_provider>
5614
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
5615
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
5616
                  const std::string_view* base_url = nullptr,
5617
                  const url_pattern_options* options = nullptr);
5618
#endif  // ADA_INCLUDE_URL_PATTERN
5619
5620
/**
5621
 * Converts a file system path to a file:// URL.
5622
 *
5623
 * Creates a properly formatted file URL from a local file system path.
5624
 * Handles platform-specific path separators and percent-encoding.
5625
 *
5626
 * Respects `get_max_input_length()`: if the input path or the resulting
5627
 * `file://` href would exceed the limit, returns an empty string.
5628
 *
5629
 * @param path The file system path to convert. Must be valid ASCII or UTF-8.
5630
 *
5631
 * @return A file:// URL string representing the given path, or empty on
5632
 *         length-limit rejection.
5633
 */
5634
std::string href_from_file(std::string_view path);
5635
5636
/**
5637
 * Sets the maximum allowed length for URLs.
5638
 *
5639
 * Both the raw input and the resulting normalized URL (the href) are checked
5640
 * against this limit. Parsing or setter calls that would produce a URL
5641
 * exceeding this length are rejected. The same limit also applies to
5642
 * `href_from_file` and to query strings passed to `url_search_params`
5643
 * construction / `reset`. The value must fit in a uint32_t.
5644
 * The default is std::numeric_limits<uint32_t>::max() (approximately 4 GB).
5645
 *
5646
 * @param length The new maximum URL length in bytes.
5647
 */
5648
void set_max_input_length(uint32_t length);
5649
5650
/**
5651
 * Returns the current maximum allowed length for URLs.
5652
 *
5653
 * @return The current maximum URL length in bytes.
5654
 */
5655
uint32_t get_max_input_length();
5656
5657
}  // namespace ada
5658
5659
#endif  // ADA_IMPLEMENTATION_H
5660
/* end file include/ada/implementation.h */
5661
5662
#include <ostream>
5663
#include <string>
5664
#include <string_view>
5665
#include <unordered_map>
5666
#include <variant>
5667
#include <vector>
5668
5669
#if ADA_TESTING
5670
#include <iostream>
5671
#endif  // ADA_TESTING
5672
5673
#if ADA_INCLUDE_URL_PATTERN
5674
namespace ada {
5675
5676
enum class url_pattern_part_type : uint8_t {
5677
  // The part represents a simple fixed text string.
5678
  FIXED_TEXT,
5679
  // The part represents a matching group with a custom regular expression.
5680
  REGEXP,
5681
  // The part represents a matching group that matches code points up to the
5682
  // next separator code point. This is typically used for a named group like
5683
  // ":foo" that does not have a custom regular expression.
5684
  SEGMENT_WILDCARD,
5685
  // The part represents a matching group that greedily matches all code points.
5686
  // This is typically used for the "*" wildcard matching group.
5687
  FULL_WILDCARD,
5688
};
5689
5690
// Pattern type for fast-path matching optimization.
5691
// This allows skipping expensive regex evaluation for common simple patterns.
5692
enum class url_pattern_component_type : uint8_t {
5693
  // Pattern is "^$" - only matches empty string
5694
  EMPTY,
5695
  // Pattern is "^<literal>$" - exact string match (no regex needed)
5696
  EXACT_MATCH,
5697
  // Pattern is "^(.*)$" - matches anything (full wildcard)
5698
  FULL_WILDCARD,
5699
  // Pattern requires actual regex evaluation
5700
  REGEXP,
5701
};
5702
5703
enum class url_pattern_part_modifier : uint8_t {
5704
  // The part does not have a modifier.
5705
  none,
5706
  // The part has an optional modifier indicated by the U+003F (?) code point.
5707
  optional,
5708
  // The part has a "zero or more" modifier indicated by the U+002A (*) code
5709
  // point.
5710
  zero_or_more,
5711
  // The part has a "one or more" modifier indicated by the U+002B (+) code
5712
  // point.
5713
  one_or_more,
5714
};
5715
5716
// @see https://urlpattern.spec.whatwg.org/#part
5717
class url_pattern_part {
5718
 public:
5719
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5720
                   url_pattern_part_modifier _modifier)
5721
0
      : type(_type), value(std::move(_value)), modifier(_modifier) {}
5722
5723
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5724
                   url_pattern_part_modifier _modifier, std::string&& _name,
5725
                   std::string&& _prefix, std::string&& _suffix)
5726
      : type(_type),
5727
        value(std::move(_value)),
5728
        modifier(_modifier),
5729
        name(std::move(_name)),
5730
        prefix(std::move(_prefix)),
5731
0
        suffix(std::move(_suffix)) {}
5732
  // A part has an associated type, a string, which must be set upon creation.
5733
  url_pattern_part_type type;
5734
  // A part has an associated value, a string, which must be set upon creation.
5735
  std::string value;
5736
  // A part has an associated modifier a string, which must be set upon
5737
  // creation.
5738
  url_pattern_part_modifier modifier;
5739
  // A part has an associated name, a string, initially the empty string.
5740
  std::string name{};
5741
  // A part has an associated prefix, a string, initially the empty string.
5742
  std::string prefix{};
5743
  // A part has an associated suffix, a string, initially the empty string.
5744
  std::string suffix{};
5745
5746
  inline bool is_regexp() const noexcept;
5747
};
5748
5749
// @see https://urlpattern.spec.whatwg.org/#options-header
5750
struct url_pattern_compile_component_options {
5751
  url_pattern_compile_component_options() = default;
5752
  explicit url_pattern_compile_component_options(
5753
      std::optional<char> new_delimiter = std::nullopt,
5754
      std::optional<char> new_prefix = std::nullopt) noexcept
5755
6
      : delimiter(new_delimiter), prefix(new_prefix) {}
5756
5757
  inline std::string_view get_delimiter() const ada_warn_unused;
5758
  inline std::string_view get_prefix() const ada_warn_unused;
5759
5760
  // @see https://urlpattern.spec.whatwg.org/#options-ignore-case
5761
  bool ignore_case = false;
5762
5763
  static url_pattern_compile_component_options DEFAULT;
5764
  static url_pattern_compile_component_options HOSTNAME;
5765
  static url_pattern_compile_component_options PATHNAME;
5766
5767
 private:
5768
  // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point
5769
  std::optional<char> delimiter{};
5770
  // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point
5771
  std::optional<char> prefix{};
5772
};
5773
5774
// The default options is an options struct with delimiter code point set to
5775
// the empty string and prefix code point set to the empty string.
5776
inline url_pattern_compile_component_options
5777
    url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt);
5778
5779
// The hostname options is an options struct with delimiter code point set
5780
// "." and prefix code point set to the empty string.
5781
inline url_pattern_compile_component_options
5782
    url_pattern_compile_component_options::HOSTNAME('.', std::nullopt);
5783
5784
// The pathname options is an options struct with delimiter code point set
5785
// "/" and prefix code point set to "/".
5786
inline url_pattern_compile_component_options
5787
    url_pattern_compile_component_options::PATHNAME('/', '/');
5788
5789
// A struct providing the URLPattern matching results for a single
5790
// URL component. The URLPatternComponentResult is only ever used
5791
// as a member attribute of a URLPatternResult struct. The
5792
// URLPatternComponentResult API is defined as part of the URLPattern
5793
// specification.
5794
struct url_pattern_component_result {
5795
  std::string input;
5796
  std::unordered_map<std::string, std::optional<std::string>> groups;
5797
5798
  bool operator==(const url_pattern_component_result&) const;
5799
5800
#if ADA_TESTING
5801
  friend void PrintTo(const url_pattern_component_result& result,
5802
                      std::ostream* os) {
5803
    *os << "input: '" << result.input << "', group: ";
5804
    for (const auto& group : result.groups) {
5805
      *os << "(" << group.first << ", " << group.second.value_or("undefined")
5806
          << ") ";
5807
    }
5808
  }
5809
#endif  // ADA_TESTING
5810
};
5811
5812
template <url_pattern_regex::regex_concept regex_provider>
5813
class url_pattern_component {
5814
 public:
5815
  url_pattern_component() = default;
5816
5817
  // This function explicitly takes a std::string because it is moved.
5818
  // To avoid unnecessary copy, move each value while calling the constructor.
5819
  url_pattern_component(std::string&& new_pattern,
5820
                        typename regex_provider::regex_type&& new_regexp,
5821
                        std::vector<std::string>&& new_group_name_list,
5822
                        bool new_has_regexp_groups,
5823
                        url_pattern_component_type new_type,
5824
                        std::string&& new_exact_match_value = {})
5825
      : regexp(std::move(new_regexp)),
5826
        pattern(std::move(new_pattern)),
5827
        group_name_list(std::move(new_group_name_list)),
5828
        exact_match_value(std::move(new_exact_match_value)),
5829
        has_regexp_groups(new_has_regexp_groups),
5830
        type(new_type) {}
5831
5832
  // @see https://urlpattern.spec.whatwg.org/#compile-a-component
5833
  template <url_pattern_encoding_callback F>
5834
  static tl::expected<url_pattern_component, errors> compile(
5835
      std::string_view input, F& encoding_callback,
5836
      url_pattern_compile_component_options& options);
5837
5838
  // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result
5839
  url_pattern_component_result create_component_match_result(
5840
      std::string&& input,
5841
      std::vector<std::optional<std::string>>&& exec_result);
5842
5843
  // Fast path test that returns true/false without constructing result groups.
5844
  // Uses cached pattern type to skip regex evaluation for simple patterns.
5845
  bool fast_test(std::string_view input) const noexcept;
5846
5847
  // Fast path match that returns capture groups without regex for simple
5848
  // patterns. Returns nullopt if pattern doesn't match, otherwise returns
5849
  // capture groups.
5850
  std::optional<std::vector<std::optional<std::string>>> fast_match(
5851
      std::string_view input) const;
5852
5853
#if ADA_TESTING
5854
  friend void PrintTo(const url_pattern_component& component,
5855
                      std::ostream* os) {
5856
    *os << "pattern: '" << component.pattern
5857
        << "', has_regexp_groups: " << component.has_regexp_groups
5858
        << "group_name_list: ";
5859
    for (const auto& name : component.group_name_list) {
5860
      *os << name << ", ";
5861
    }
5862
  }
5863
#endif  // ADA_TESTING
5864
5865
  typename regex_provider::regex_type regexp{};
5866
  std::string pattern{};
5867
  std::vector<std::string> group_name_list{};
5868
  // For EXACT_MATCH type: the literal string to compare against
5869
  std::string exact_match_value{};
5870
  bool has_regexp_groups = false;
5871
  // Cached pattern type for fast-path optimization
5872
  url_pattern_component_type type = url_pattern_component_type::REGEXP;
5873
};
5874
5875
// A URLPattern input can be either a string or a URLPatternInit object.
5876
// If it is a string, it must be a valid UTF-8 string.
5877
using url_pattern_input = std::variant<std::string_view, url_pattern_init>;
5878
5879
// A struct providing the URLPattern matching results for all
5880
// components of a URL. The URLPatternResult API is defined as
5881
// part of the URLPattern specification.
5882
struct url_pattern_result {
5883
  std::vector<url_pattern_input> inputs;
5884
  url_pattern_component_result protocol;
5885
  url_pattern_component_result username;
5886
  url_pattern_component_result password;
5887
  url_pattern_component_result hostname;
5888
  url_pattern_component_result port;
5889
  url_pattern_component_result pathname;
5890
  url_pattern_component_result search;
5891
  url_pattern_component_result hash;
5892
};
5893
5894
struct url_pattern_options {
5895
  bool ignore_case = false;
5896
5897
#if ADA_TESTING
5898
  friend void PrintTo(const url_pattern_options& options, std::ostream* os) {
5899
    *os << "ignore_case: '" << options.ignore_case;
5900
  }
5901
#endif  // ADA_TESTING
5902
};
5903
5904
/**
5905
 * @brief URL pattern matching class implementing the URLPattern API.
5906
 *
5907
 * URLPattern provides a way to match URLs against patterns with wildcards
5908
 * and named capture groups. It's useful for routing, URL-based dispatching,
5909
 * and URL validation.
5910
 *
5911
 * Pattern syntax supports:
5912
 * - Literal text matching
5913
 * - Named groups: `:name` (matches up to the next separator)
5914
 * - Wildcards: `*` (matches everything)
5915
 * - Custom regex: `(pattern)`
5916
 * - Optional segments: `:name?`
5917
 * - Repeated segments: `:name+`, `:name*`
5918
 *
5919
 * @tparam regex_provider The regex implementation to use for pattern matching.
5920
 *         Must satisfy the url_pattern_regex::regex_concept.
5921
 *
5922
 * @note All string inputs must be valid UTF-8.
5923
 *
5924
 * @see https://urlpattern.spec.whatwg.org/
5925
 */
5926
template <url_pattern_regex::regex_concept regex_provider>
5927
class url_pattern {
5928
 public:
5929
  url_pattern() = default;
5930
5931
  /**
5932
   * If non-null, base_url must pointer at a valid UTF-8 string.
5933
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec
5934
   */
5935
  result<std::optional<url_pattern_result>> exec(
5936
      const url_pattern_input& input,
5937
      const std::string_view* base_url = nullptr);
5938
5939
  /**
5940
   * If non-null, base_url must pointer at a valid UTF-8 string.
5941
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
5942
   */
5943
  result<bool> test(const url_pattern_input& input,
5944
                    const std::string_view* base_url = nullptr);
5945
5946
  /**
5947
   * @see https://urlpattern.spec.whatwg.org/#url-pattern-match
5948
   * This function expects a valid UTF-8 string if input is a string.
5949
   */
5950
  result<std::optional<url_pattern_result>> match(
5951
      const url_pattern_input& input,
5952
      const std::string_view* base_url_string = nullptr);
5953
5954
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol
5955
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
5956
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username
5957
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
5958
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password
5959
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
5960
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname
5961
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
5962
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port
5963
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
5964
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname
5965
  [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound;
5966
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search
5967
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
5968
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash
5969
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
5970
5971
  // If ignoreCase is true, the JavaScript regular expression created for each
5972
  // pattern must use the `vi` flag. Otherwise, they must use the `v` flag.
5973
  [[nodiscard]] bool ignore_case() const;
5974
5975
  // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups
5976
  [[nodiscard]] bool has_regexp_groups() const;
5977
5978
  // Helper to test all components at once. Returns true if all match.
5979
  [[nodiscard]] bool test_components(
5980
      std::string_view protocol, std::string_view username,
5981
      std::string_view password, std::string_view hostname,
5982
      std::string_view port, std::string_view pathname, std::string_view search,
5983
      std::string_view hash) const;
5984
5985
#if ADA_TESTING
5986
  friend void PrintTo(const url_pattern& c, std::ostream* os) {
5987
    *os << "protocol_component: '" << c.get_protocol() << ", ";
5988
    *os << "username_component: '" << c.get_username() << ", ";
5989
    *os << "password_component: '" << c.get_password() << ", ";
5990
    *os << "hostname_component: '" << c.get_hostname() << ", ";
5991
    *os << "port_component: '" << c.get_port() << ", ";
5992
    *os << "pathname_component: '" << c.get_pathname() << ", ";
5993
    *os << "search_component: '" << c.get_search() << ", ";
5994
    *os << "hash_component: '" << c.get_hash();
5995
  }
5996
#endif  // ADA_TESTING
5997
5998
  template <url_pattern_regex::regex_concept P>
5999
  friend tl::expected<url_pattern<P>, errors> parser::parse_url_pattern_impl(
6000
      std::variant<std::string_view, url_pattern_init>&& input,
6001
      const std::string_view* base_url, const url_pattern_options* options);
6002
6003
  /**
6004
   * @private
6005
   * We can not make this private due to a LLVM bug.
6006
   * Ref: https://github.com/ada-url/ada/pull/859
6007
   */
6008
  url_pattern_component<regex_provider> protocol_component{};
6009
  /**
6010
   * @private
6011
   * We can not make this private due to a LLVM bug.
6012
   * Ref: https://github.com/ada-url/ada/pull/859
6013
   */
6014
  url_pattern_component<regex_provider> username_component{};
6015
  /**
6016
   * @private
6017
   * We can not make this private due to a LLVM bug.
6018
   * Ref: https://github.com/ada-url/ada/pull/859
6019
   */
6020
  url_pattern_component<regex_provider> password_component{};
6021
  /**
6022
   * @private
6023
   * We can not make this private due to a LLVM bug.
6024
   * Ref: https://github.com/ada-url/ada/pull/859
6025
   */
6026
  url_pattern_component<regex_provider> hostname_component{};
6027
  /**
6028
   * @private
6029
   * We can not make this private due to a LLVM bug.
6030
   * Ref: https://github.com/ada-url/ada/pull/859
6031
   */
6032
  url_pattern_component<regex_provider> port_component{};
6033
  /**
6034
   * @private
6035
   * We can not make this private due to a LLVM bug.
6036
   * Ref: https://github.com/ada-url/ada/pull/859
6037
   */
6038
  url_pattern_component<regex_provider> pathname_component{};
6039
  /**
6040
   * @private
6041
   * We can not make this private due to a LLVM bug.
6042
   * Ref: https://github.com/ada-url/ada/pull/859
6043
   */
6044
  url_pattern_component<regex_provider> search_component{};
6045
  /**
6046
   * @private
6047
   * We can not make this private due to a LLVM bug.
6048
   * Ref: https://github.com/ada-url/ada/pull/859
6049
   */
6050
  url_pattern_component<regex_provider> hash_component{};
6051
  /**
6052
   * @private
6053
   * We can not make this private due to a LLVM bug.
6054
   * Ref: https://github.com/ada-url/ada/pull/859
6055
   */
6056
  bool ignore_case_ = false;
6057
};
6058
}  // namespace ada
6059
#endif  // ADA_INCLUDE_URL_PATTERN
6060
#endif
6061
/* end file include/ada/url_pattern.h */
6062
/* begin file include/ada/url_pattern_helpers.h */
6063
/**
6064
 * @file url_pattern_helpers.h
6065
 * @brief Declaration for the URLPattern helpers.
6066
 */
6067
#ifndef ADA_URL_PATTERN_HELPERS_H
6068
#define ADA_URL_PATTERN_HELPERS_H
6069
6070
6071
#include <string>
6072
#include <tuple>
6073
#include <vector>
6074
6075
#if ADA_INCLUDE_URL_PATTERN
6076
namespace ada {
6077
enum class errors : uint8_t;
6078
}
6079
6080
namespace ada::url_pattern_helpers {
6081
6082
// @see https://urlpattern.spec.whatwg.org/#token
6083
enum class token_type : uint8_t {
6084
  INVALID_CHAR,    // 0
6085
  OPEN,            // 1
6086
  CLOSE,           // 2
6087
  REGEXP,          // 3
6088
  NAME,            // 4
6089
  CHAR,            // 5
6090
  ESCAPED_CHAR,    // 6
6091
  OTHER_MODIFIER,  // 7
6092
  ASTERISK,        // 8
6093
  END,             // 9
6094
};
6095
6096
#ifdef ADA_TESTING
6097
std::string to_string(token_type type);
6098
#endif  // ADA_TESTING
6099
6100
// @see https://urlpattern.spec.whatwg.org/#tokenize-policy
6101
enum class token_policy {
6102
  strict,
6103
  lenient,
6104
};
6105
6106
// @see https://urlpattern.spec.whatwg.org/#tokens
6107
class token {
6108
 public:
6109
  token(token_type _type, size_t _index, std::string_view _value)
6110
0
      : type(_type), index(_index), value(_value) {}
6111
6112
  // A token has an associated type, a string, initially "invalid-char".
6113
  token_type type = token_type::INVALID_CHAR;
6114
6115
  // A token has an associated index, a number, initially 0. It is the position
6116
  // of the first code point in the pattern string represented by the token.
6117
  size_t index = 0;
6118
6119
  // A token has an associated value, a string, initially the empty string. It
6120
  // contains the code points from the pattern string represented by the token.
6121
  std::string_view value{};
6122
};
6123
6124
// @see https://urlpattern.spec.whatwg.org/#pattern-parser
6125
template <url_pattern_encoding_callback F>
6126
class url_pattern_parser {
6127
 public:
6128
  url_pattern_parser(F& encoding_callback_,
6129
                     std::string_view segment_wildcard_regexp_)
6130
      : encoding_callback(encoding_callback_),
6131
        segment_wildcard_regexp(segment_wildcard_regexp_) {}
6132
6133
  bool can_continue() const { return index < tokens.size(); }
6134
6135
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token
6136
  token* try_consume_token(token_type type);
6137
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token
6138
  token* try_consume_modifier_token();
6139
  // @see
6140
  // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token
6141
  token* try_consume_regexp_or_wildcard_token(const token* name_token);
6142
  // @see https://urlpattern.spec.whatwg.org/#consume-text
6143
  std::string consume_text();
6144
  // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token
6145
  bool consume_required_token(token_type type);
6146
  // @see
6147
  // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value
6148
  std::optional<errors> maybe_add_part_from_the_pending_fixed_value()
6149
      ada_warn_unused;
6150
  // @see https://urlpattern.spec.whatwg.org/#add-a-part
6151
  std::optional<errors> add_part(std::string_view prefix, token* name_token,
6152
                                 token* regexp_or_wildcard_token,
6153
                                 std::string_view suyffix,
6154
                                 token* modifier_token) ada_warn_unused;
6155
6156
  std::vector<token> tokens{};
6157
  F& encoding_callback;
6158
  std::string segment_wildcard_regexp;
6159
  std::vector<url_pattern_part> parts{};
6160
  std::string pending_fixed_value{};
6161
  size_t index = 0;
6162
  size_t next_numeric_name = 0;
6163
};
6164
6165
// @see https://urlpattern.spec.whatwg.org/#tokenizer
6166
class Tokenizer {
6167
 public:
6168
  explicit Tokenizer(std::string_view new_input, token_policy new_policy)
6169
0
      : input(new_input), policy(new_policy) {}
6170
6171
  // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point
6172
  constexpr void get_next_code_point();
6173
6174
  // True when the most recent decoded unit was malformed UTF-8.
6175
0
  bool had_invalid_code_point() const { return invalid_code_point; }
6176
6177
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
6178
  constexpr void seek_and_get_next_code_point(size_t index);
6179
6180
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
6181
6182
  void add_token(token_type type, size_t next_position, size_t value_position,
6183
                 size_t value_length);
6184
6185
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
6186
  void add_token_with_default_length(token_type type, size_t next_position,
6187
                                     size_t value_position);
6188
6189
  // @see
6190
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
6191
  void add_token_with_defaults(token_type type);
6192
6193
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
6194
  std::optional<errors> process_tokenizing_error(
6195
      size_t next_position, size_t value_position) ada_warn_unused;
6196
6197
  friend tl::expected<std::vector<token>, errors> tokenize(
6198
      std::string_view input, token_policy policy);
6199
6200
 private:
6201
  // has an associated input, a pattern string, initially the empty string.
6202
  std::string_view input;
6203
  // has an associated policy, a tokenize policy, initially "strict".
6204
  token_policy policy;
6205
  // has an associated token list, a token list, initially an empty list.
6206
  std::vector<token> token_list{};
6207
  // has an associated index, a number, initially 0.
6208
  size_t index = 0;
6209
  // has an associated next index, a number, initially 0.
6210
  size_t next_index = 0;
6211
  // has an associated code point, a Unicode code point, initially null.
6212
  char32_t code_point{};
6213
  // Tracks whether the last decoded code point was malformed UTF-8.
6214
  bool invalid_code_point = false;
6215
};
6216
6217
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
6218
template <url_pattern_regex::regex_concept regex_provider>
6219
struct constructor_string_parser {
6220
  explicit constructor_string_parser(std::string_view new_input,
6221
                                     std::vector<token>&& new_token_list)
6222
      : input(new_input), token_list(std::move(new_token_list)) {}
6223
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
6224
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
6225
6226
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
6227
  enum class State {
6228
    INIT,
6229
    PROTOCOL,
6230
    AUTHORITY,
6231
    USERNAME,
6232
    PASSWORD,
6233
    HOSTNAME,
6234
    PORT,
6235
    PATHNAME,
6236
    SEARCH,
6237
    HASH,
6238
    DONE,
6239
  };
6240
6241
  // @see
6242
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6243
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6244
6245
 private:
6246
  // @see https://urlpattern.spec.whatwg.org/#rewind
6247
  constexpr void rewind();
6248
6249
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6250
  constexpr bool is_hash_prefix();
6251
6252
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6253
  constexpr bool is_search_prefix();
6254
6255
  // @see https://urlpattern.spec.whatwg.org/#change-state
6256
  void change_state(State state, size_t skip);
6257
6258
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6259
  constexpr bool is_group_open() const;
6260
6261
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6262
  constexpr bool is_group_close() const;
6263
6264
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6265
  constexpr bool is_protocol_suffix() const;
6266
6267
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6268
  constexpr bool next_is_authority_slashes() const;
6269
6270
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6271
  constexpr bool is_an_identity_terminator() const;
6272
6273
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6274
  constexpr bool is_pathname_start() const;
6275
6276
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6277
  constexpr bool is_password_prefix() const;
6278
6279
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6280
  constexpr bool is_an_ipv6_open() const;
6281
6282
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6283
  constexpr bool is_an_ipv6_close() const;
6284
6285
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6286
  constexpr bool is_port_prefix() const;
6287
6288
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6289
  constexpr bool is_non_special_pattern_char(size_t index,
6290
                                             uint32_t value) const;
6291
6292
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6293
  constexpr const token* get_safe_token(size_t index) const;
6294
6295
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6296
  std::string make_component_string();
6297
  // has an associated input, a string, which must be set upon creation.
6298
  std::string_view input;
6299
  // has an associated token list, a token list, which must be set upon
6300
  // creation.
6301
  std::vector<token> token_list;
6302
  // has an associated result, a URLPatternInit, initially set to a new
6303
  // URLPatternInit.
6304
  url_pattern_init result{};
6305
  // has an associated component start, a number, initially set to 0.
6306
  size_t component_start = 0;
6307
  // has an associated token index, a number, initially set to 0.
6308
  size_t token_index = 0;
6309
  // has an associated token increment, a number, initially set to 1.
6310
  size_t token_increment = 1;
6311
  // has an associated group depth, a number, initially set to 0.
6312
  size_t group_depth = 0;
6313
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6314
  // 0.
6315
  size_t hostname_ipv6_bracket_depth = 0;
6316
  // has an associated protocol matches a special scheme flag, a boolean,
6317
  // initially set to false.
6318
  bool protocol_matches_a_special_scheme_flag = false;
6319
  // has an associated state, a string, initially set to "init".
6320
  State state = State::INIT;
6321
};
6322
6323
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6324
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6325
6326
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6327
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6328
6329
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6330
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6331
6332
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6333
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6334
6335
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6336
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6337
    std::string_view input);
6338
6339
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6340
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6341
6342
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6343
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6344
    std::string_view input, std::string_view protocol);
6345
6346
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6347
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6348
6349
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6350
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6351
    std::string_view input);
6352
6353
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6354
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6355
6356
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6357
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6358
6359
// @see https://urlpattern.spec.whatwg.org/#tokenize
6360
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6361
                                                  token_policy policy);
6362
6363
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6364
std::string process_base_url_string(std::string_view input,
6365
                                    url_pattern_init::process_type type);
6366
6367
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6368
std::string escape_pattern_string(std::string_view input);
6369
6370
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6371
std::string escape_regexp_string(std::string_view input);
6372
6373
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6374
constexpr bool is_absolute_pathname(
6375
    std::string_view input, url_pattern_init::process_type type) noexcept;
6376
6377
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6378
template <url_pattern_encoding_callback F>
6379
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6380
    std::string_view input, url_pattern_compile_component_options& options,
6381
    F& encoding_callback);
6382
6383
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6384
std::string generate_pattern_string(
6385
    std::vector<url_pattern_part>& part_list,
6386
    url_pattern_compile_component_options& options);
6387
6388
// @see
6389
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6390
std::tuple<std::string, std::vector<std::string>>
6391
generate_regular_expression_and_name_list(
6392
    const std::vector<url_pattern_part>& part_list,
6393
    url_pattern_compile_component_options options);
6394
6395
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6396
bool is_ipv6_address(std::string_view input) noexcept;
6397
6398
// @see
6399
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6400
template <url_pattern_regex::regex_concept regex_provider>
6401
bool protocol_component_matches_special_scheme(
6402
    ada::url_pattern_component<regex_provider>& input);
6403
6404
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6405
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6406
6407
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6408
std::string generate_segment_wildcard_regexp(
6409
    url_pattern_compile_component_options options);
6410
6411
}  // namespace ada::url_pattern_helpers
6412
#endif  // ADA_INCLUDE_URL_PATTERN
6413
#endif
6414
/* end file include/ada/url_pattern_helpers.h */
6415
6416
#include <string>
6417
#include <string_view>
6418
#include <variant>
6419
6420
namespace ada::parser {
6421
#if ADA_INCLUDE_URL_PATTERN
6422
template <url_pattern_regex::regex_concept regex_provider>
6423
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6424
    std::variant<std::string_view, url_pattern_init>&& input,
6425
    const std::string_view* base_url, const url_pattern_options* options) {
6426
  // Let init be null.
6427
  url_pattern_init init;
6428
6429
  // If input is a scalar value string then:
6430
  if (std::holds_alternative<std::string_view>(input)) {
6431
    // Set init to the result of running parse a constructor string given input.
6432
    auto parse_result =
6433
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6434
            std::get<std::string_view>(input));
6435
    if (!parse_result) {
6436
      ada_log("constructor_string_parser::parse failed");
6437
      return tl::unexpected(parse_result.error());
6438
    }
6439
    init = std::move(*parse_result);
6440
    // If baseURL is null and init["protocol"] does not exist, then throw a
6441
    // TypeError.
6442
    if (!base_url && !init.protocol) {
6443
      ada_log("base url is null and protocol is not set");
6444
      return tl::unexpected(errors::type_error);
6445
    }
6446
6447
    // If baseURL is not null, set init["baseURL"] to baseURL.
6448
    if (base_url) {
6449
      init.base_url = std::string(*base_url);
6450
    }
6451
  } else {
6452
    // Assert: input is a URLPatternInit.
6453
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6454
    // If baseURL is not null, then throw a TypeError.
6455
    if (base_url) {
6456
      ada_log("base url is not null");
6457
      return tl::unexpected(errors::type_error);
6458
    }
6459
    // Optimization: Avoid copy by moving the input value.
6460
    // Set init to input.
6461
    init = std::move(std::get<url_pattern_init>(input));
6462
  }
6463
6464
  // Let processedInit be the result of process a URLPatternInit given init,
6465
  // "pattern", null, null, null, null, null, null, null, and null.
6466
  auto processed_init =
6467
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6468
  if (!processed_init) {
6469
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6470
    return tl::unexpected(processed_init.error());
6471
  }
6472
6473
  // For each componentName of  "protocol", "username", "password", "hostname",
6474
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6475
  // not exist, then set processedInit[componentName] to "*".
6476
  ADA_ASSERT_TRUE(processed_init.has_value());
6477
  if (!processed_init->protocol) processed_init->protocol = "*";
6478
  if (!processed_init->username) processed_init->username = "*";
6479
  if (!processed_init->password) processed_init->password = "*";
6480
  if (!processed_init->hostname) processed_init->hostname = "*";
6481
  if (!processed_init->port) processed_init->port = "*";
6482
  if (!processed_init->pathname) processed_init->pathname = "*";
6483
  if (!processed_init->search) processed_init->search = "*";
6484
  if (!processed_init->hash) processed_init->hash = "*";
6485
6486
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6487
  ada_log("-- processed_init->username: ", processed_init->username.value());
6488
  ada_log("-- processed_init->password: ", processed_init->password.value());
6489
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6490
  ada_log("-- processed_init->port: ", processed_init->port.value());
6491
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6492
  ada_log("-- processed_init->search: ", processed_init->search.value());
6493
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6494
6495
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6496
  // is a string which represents its corresponding default port in radix-10
6497
  // using ASCII digits then set processedInit["port"] to the empty string.
6498
  // TODO: Optimization opportunity.
6499
  if (scheme::is_special(*processed_init->protocol)) {
6500
    // file is the only special scheme with no default port; get_special_port
6501
    // returns 0 as its sentinel, so a literal "0" port must not be mistaken for
6502
    // a default and dropped.
6503
    uint16_t default_port = scheme::get_special_port(*processed_init->protocol);
6504
    if (default_port != 0 &&
6505
        std::to_string(default_port) == processed_init->port.value()) {
6506
      processed_init->port->clear();
6507
    }
6508
  }
6509
6510
  // Let urlPattern be a new URL pattern.
6511
  url_pattern<regex_provider> url_pattern_{};
6512
6513
  // Set urlPattern's protocol component to the result of compiling a component
6514
  // given processedInit["protocol"], canonicalize a protocol, and default
6515
  // options.
6516
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6517
      processed_init->protocol.value(),
6518
      url_pattern_helpers::canonicalize_protocol,
6519
      url_pattern_compile_component_options::DEFAULT);
6520
  if (!protocol_component) {
6521
    ada_log("url_pattern_component::compile failed for protocol ",
6522
            processed_init->protocol.value());
6523
    return tl::unexpected(protocol_component.error());
6524
  }
6525
  url_pattern_.protocol_component = std::move(*protocol_component);
6526
6527
  // Set urlPattern's username component to the result of compiling a component
6528
  // given processedInit["username"], canonicalize a username, and default
6529
  // options.
6530
  auto username_component = url_pattern_component<regex_provider>::compile(
6531
      processed_init->username.value(),
6532
      url_pattern_helpers::canonicalize_username,
6533
      url_pattern_compile_component_options::DEFAULT);
6534
  if (!username_component) {
6535
    ada_log("url_pattern_component::compile failed for username ",
6536
            processed_init->username.value());
6537
    return tl::unexpected(username_component.error());
6538
  }
6539
  url_pattern_.username_component = std::move(*username_component);
6540
6541
  // Set urlPattern's password component to the result of compiling a component
6542
  // given processedInit["password"], canonicalize a password, and default
6543
  // options.
6544
  auto password_component = url_pattern_component<regex_provider>::compile(
6545
      processed_init->password.value(),
6546
      url_pattern_helpers::canonicalize_password,
6547
      url_pattern_compile_component_options::DEFAULT);
6548
  if (!password_component) {
6549
    ada_log("url_pattern_component::compile failed for password ",
6550
            processed_init->password.value());
6551
    return tl::unexpected(password_component.error());
6552
  }
6553
  url_pattern_.password_component = std::move(*password_component);
6554
6555
  // TODO: Optimization opportunity. The following if statement can be
6556
  // simplified.
6557
  // If the result running hostname pattern is an IPv6 address given
6558
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6559
  // to the result of compiling a component given processedInit["hostname"],
6560
  // canonicalize an IPv6 hostname, and hostname options.
6561
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6562
    ada_log("processed_init->hostname is ipv6 address");
6563
    // then set urlPattern's hostname component to the result of compiling a
6564
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6565
    // and hostname options.
6566
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6567
        processed_init->hostname.value(),
6568
        url_pattern_helpers::canonicalize_ipv6_hostname,
6569
        url_pattern_compile_component_options::DEFAULT);
6570
    if (!hostname_component) {
6571
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6572
              processed_init->hostname.value());
6573
      return tl::unexpected(hostname_component.error());
6574
    }
6575
    url_pattern_.hostname_component = std::move(*hostname_component);
6576
  } else {
6577
    // Otherwise, set urlPattern's hostname component to the result of compiling
6578
    // a component given processedInit["hostname"], canonicalize a hostname, and
6579
    // hostname options.
6580
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6581
        processed_init->hostname.value(),
6582
        url_pattern_helpers::canonicalize_hostname,
6583
        url_pattern_compile_component_options::HOSTNAME);
6584
    if (!hostname_component) {
6585
      ada_log("url_pattern_component::compile failed for hostname ",
6586
              processed_init->hostname.value());
6587
      return tl::unexpected(hostname_component.error());
6588
    }
6589
    url_pattern_.hostname_component = std::move(*hostname_component);
6590
  }
6591
6592
  // Set urlPattern's port component to the result of compiling a component
6593
  // given processedInit["port"], canonicalize a port, and default options.
6594
  auto port_component = url_pattern_component<regex_provider>::compile(
6595
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6596
      url_pattern_compile_component_options::DEFAULT);
6597
  if (!port_component) {
6598
    ada_log("url_pattern_component::compile failed for port ",
6599
            processed_init->port.value());
6600
    return tl::unexpected(port_component.error());
6601
  }
6602
  url_pattern_.port_component = std::move(*port_component);
6603
6604
  // Let compileOptions be a copy of the default options with the ignore case
6605
  // property set to options["ignoreCase"].
6606
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6607
  if (options) {
6608
    compile_options.ignore_case = options->ignore_case;
6609
  }
6610
6611
  // TODO: Optimization opportunity: Simplify this if statement.
6612
  // If the result of running protocol component matches a special scheme given
6613
  // urlPattern's protocol component is true, then:
6614
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6615
          regex_provider>(url_pattern_.protocol_component)) {
6616
    // Let pathCompileOptions be copy of the pathname options with the ignore
6617
    // case property set to options["ignoreCase"].
6618
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6619
    if (options) {
6620
      path_compile_options.ignore_case = options->ignore_case;
6621
    }
6622
6623
    // Set urlPattern's pathname component to the result of compiling a
6624
    // component given processedInit["pathname"], canonicalize a pathname, and
6625
    // pathCompileOptions.
6626
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6627
        processed_init->pathname.value(),
6628
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6629
    if (!pathname_component) {
6630
      ada_log("url_pattern_component::compile failed for pathname ",
6631
              processed_init->pathname.value());
6632
      return tl::unexpected(pathname_component.error());
6633
    }
6634
    url_pattern_.pathname_component = std::move(*pathname_component);
6635
  } else {
6636
    // Otherwise set urlPattern's pathname component to the result of compiling
6637
    // a component given processedInit["pathname"], canonicalize an opaque
6638
    // pathname, and compileOptions.
6639
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6640
        processed_init->pathname.value(),
6641
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6642
    if (!pathname_component) {
6643
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6644
              processed_init->pathname.value());
6645
      return tl::unexpected(pathname_component.error());
6646
    }
6647
    url_pattern_.pathname_component = std::move(*pathname_component);
6648
  }
6649
6650
  // Set urlPattern's search component to the result of compiling a component
6651
  // given processedInit["search"], canonicalize a search, and compileOptions.
6652
  auto search_component = url_pattern_component<regex_provider>::compile(
6653
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6654
      compile_options);
6655
  if (!search_component) {
6656
    ada_log("url_pattern_component::compile failed for search ",
6657
            processed_init->search.value());
6658
    return tl::unexpected(search_component.error());
6659
  }
6660
  url_pattern_.search_component = std::move(*search_component);
6661
6662
  // Set urlPattern's hash component to the result of compiling a component
6663
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6664
  auto hash_component = url_pattern_component<regex_provider>::compile(
6665
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6666
      compile_options);
6667
  if (!hash_component) {
6668
    ada_log("url_pattern_component::compile failed for hash ",
6669
            processed_init->hash.value());
6670
    return tl::unexpected(hash_component.error());
6671
  }
6672
  url_pattern_.hash_component = std::move(*hash_component);
6673
6674
  // Return urlPattern.
6675
  return url_pattern_;
6676
}
6677
#endif  // ADA_INCLUDE_URL_PATTERN
6678
6679
}  // namespace ada::parser
6680
6681
#endif  // ADA_PARSER_INL_H
6682
/* end file include/ada/parser-inl.h */
6683
/* begin file include/ada/scheme-inl.h */
6684
/**
6685
 * @file scheme-inl.h
6686
 * @brief Definitions for the URL scheme.
6687
 */
6688
#ifndef ADA_SCHEME_INL_H
6689
#define ADA_SCHEME_INL_H
6690
6691
6692
namespace ada::scheme {
6693
6694
/**
6695
 * @namespace ada::scheme::details
6696
 * @brief Includes the definitions for scheme specific entities
6697
 */
6698
namespace details {
6699
// for use with is_special and get_special_port
6700
// Spaces, if present, are removed from URL.
6701
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6702
                                                "ftp",  "wss", "file",  " "};
6703
// for use with get_special_port
6704
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6705
6706
// @private
6707
// convert a string_view to a 64-bit integer key for fast comparison
6708
0
constexpr uint64_t make_key(std::string_view sv) {
6709
0
  uint64_t val = 0;
6710
0
  for (size_t i = 0; i < sv.size(); i++)
6711
0
    val |= (uint64_t)(uint8_t)sv[i] << (i * 8);
6712
0
  return val;
6713
0
}
6714
// precomputed keys for the special schemes, indexed by a hash of the input
6715
// string
6716
constexpr uint64_t scheme_keys[] = {
6717
    make_key("http"),   // 0: HTTP
6718
    0,                  // 1: sentinel
6719
    make_key("https"),  // 2: HTTPS
6720
    make_key("ws"),     // 3: WS
6721
    make_key("ftp"),    // 4: FTP
6722
    make_key("wss"),    // 5: WSS
6723
    make_key("file"),   // 6: FILE
6724
    0,                  // 7: sentinel
6725
};
6726
6727
// @private
6728
// branchless load of up to 5 characters into a uint64_t, padding with zeros if
6729
// n < 5
6730
231k
inline uint64_t branchless_load5(const char* p, size_t n) {
6731
231k
  uint64_t input = (uint8_t)p[0];
6732
231k
  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
6733
231k
  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
6734
231k
  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
6735
231k
  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
6736
231k
  return input;
6737
231k
}
6738
}  // namespace details
6739
6740
/****
6741
 * @private
6742
 * In is_special, get_scheme_type, and get_special_port, we
6743
 * use a standard hashing technique to find the index of the scheme in
6744
 * the is_special_list. The hashing technique is based on the size of
6745
 * the scheme and the first character of the scheme. It ensures that we
6746
 * do at most one string comparison per call. If the protocol is
6747
 * predictible (e.g., it is always "http"), we can get a better average
6748
 * performance by using a simpler approach where we loop and compare
6749
 * scheme with all possible protocols starting with the most likely
6750
 * protocol. Doing multiple comparisons may have a poor worst case
6751
 * performance, however. In this instance, we choose a potentially
6752
 * slightly lower best-case performance for a better worst-case
6753
 * performance. We can revisit this choice at any time.
6754
 *
6755
 * Reference:
6756
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6757
 * More C++ gems 17 (2000).
6758
 *
6759
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6760
 *
6761
 * Reference: https://github.com/ada-url/ada/issues/617
6762
 ****/
6763
6764
54.8k
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6765
54.8k
  if (scheme.empty()) {
6766
0
    return false;
6767
0
  }
6768
54.8k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6769
54.8k
  const std::string_view target = details::is_special_list[hash_value];
6770
54.8k
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6771
54.8k
}
6772
0
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6773
0
  if (scheme.empty()) {
6774
0
    return 0;
6775
0
  }
6776
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6777
0
  const std::string_view target = details::is_special_list[hash_value];
6778
0
  if (scheme.size() == target.size() &&
6779
0
      details::branchless_load5(scheme.data(), scheme.size()) ==
6780
0
          details::scheme_keys[hash_value]) {
6781
0
    return details::special_ports[hash_value];
6782
0
  } else {
6783
0
    return 0;
6784
0
  }
6785
0
}
6786
47.8k
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6787
47.8k
  return details::special_ports[int(type)];
6788
47.8k
}
6789
298k
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6790
298k
  if (scheme.empty()) {
6791
0
    return ada::scheme::NOT_SPECIAL;
6792
0
  }
6793
298k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6794
298k
  const std::string_view target = details::is_special_list[hash_value];
6795
298k
  if (scheme.size() == target.size() &&
6796
231k
      details::branchless_load5(scheme.data(), scheme.size()) ==
6797
231k
          details::scheme_keys[hash_value]) {
6798
221k
    return ada::scheme::type(hash_value);
6799
221k
  } else {
6800
77.4k
    return ada::scheme::NOT_SPECIAL;
6801
77.4k
  }
6802
298k
}
6803
6804
}  // namespace ada::scheme
6805
6806
#endif  // ADA_SCHEME_INL_H
6807
/* end file include/ada/scheme-inl.h */
6808
/* begin file include/ada/serializers.h */
6809
/**
6810
 * @file serializers.h
6811
 * @brief IP address serialization utilities.
6812
 *
6813
 * This header provides functions for converting IP addresses to their
6814
 * string representations according to the WHATWG URL Standard.
6815
 */
6816
#ifndef ADA_SERIALIZERS_H
6817
#define ADA_SERIALIZERS_H
6818
6819
6820
#include <array>
6821
#include <string>
6822
6823
/**
6824
 * @namespace ada::serializers
6825
 * @brief IP address serialization functions.
6826
 *
6827
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6828
 */
6829
namespace ada::serializers {
6830
6831
/**
6832
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6833
 * Used for :: compression in IPv6 serialization.
6834
 *
6835
 * @param address The 8 16-bit pieces of the IPv6 address.
6836
 * @param[out] compress Index of the start of the longest zero sequence.
6837
 * @param[out] compress_length Length of the longest zero sequence.
6838
 */
6839
void find_longest_sequence_of_ipv6_pieces(
6840
    const std::array<uint16_t, 8>& address, size_t& compress,
6841
    size_t& compress_length) noexcept;
6842
6843
/**
6844
 * Serializes an IPv6 address to its string representation.
6845
 *
6846
 * @param address The 8 16-bit pieces of the IPv6 address.
6847
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6848
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6849
 */
6850
std::string ipv6(const std::array<uint16_t, 8>& address);
6851
6852
/**
6853
 * Serializes an IPv4 address to its dotted-decimal string representation.
6854
 *
6855
 * @param address The 32-bit IPv4 address as an integer.
6856
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6857
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6858
 */
6859
std::string ipv4(uint64_t address);
6860
6861
}  // namespace ada::serializers
6862
6863
#endif  // ADA_SERIALIZERS_H
6864
/* end file include/ada/serializers.h */
6865
/* begin file include/ada/state.h */
6866
/**
6867
 * @file state.h
6868
 * @brief URL parser state machine states.
6869
 *
6870
 * Defines the states used by the URL parsing state machine as specified
6871
 * in the WHATWG URL Standard.
6872
 *
6873
 * @see https://url.spec.whatwg.org/#url-parsing
6874
 */
6875
#ifndef ADA_STATE_H
6876
#define ADA_STATE_H
6877
6878
6879
#include <string>
6880
6881
namespace ada {
6882
6883
/**
6884
 * @brief States in the URL parsing state machine.
6885
 *
6886
 * The URL parser processes input through a sequence of states, each handling
6887
 * a specific part of the URL syntax.
6888
 *
6889
 * @see https://url.spec.whatwg.org/#url-parsing
6890
 */
6891
enum class state {
6892
  /**
6893
   * @see https://url.spec.whatwg.org/#authority-state
6894
   */
6895
  AUTHORITY,
6896
6897
  /**
6898
   * @see https://url.spec.whatwg.org/#scheme-start-state
6899
   */
6900
  SCHEME_START,
6901
6902
  /**
6903
   * @see https://url.spec.whatwg.org/#scheme-state
6904
   */
6905
  SCHEME,
6906
6907
  /**
6908
   * @see https://url.spec.whatwg.org/#host-state
6909
   */
6910
  HOST,
6911
6912
  /**
6913
   * @see https://url.spec.whatwg.org/#no-scheme-state
6914
   */
6915
  NO_SCHEME,
6916
6917
  /**
6918
   * @see https://url.spec.whatwg.org/#fragment-state
6919
   */
6920
  FRAGMENT,
6921
6922
  /**
6923
   * @see https://url.spec.whatwg.org/#relative-state
6924
   */
6925
  RELATIVE_SCHEME,
6926
6927
  /**
6928
   * @see https://url.spec.whatwg.org/#relative-slash-state
6929
   */
6930
  RELATIVE_SLASH,
6931
6932
  /**
6933
   * @see https://url.spec.whatwg.org/#file-state
6934
   */
6935
  FILE,
6936
6937
  /**
6938
   * @see https://url.spec.whatwg.org/#file-host-state
6939
   */
6940
  FILE_HOST,
6941
6942
  /**
6943
   * @see https://url.spec.whatwg.org/#file-slash-state
6944
   */
6945
  FILE_SLASH,
6946
6947
  /**
6948
   * @see https://url.spec.whatwg.org/#path-or-authority-state
6949
   */
6950
  PATH_OR_AUTHORITY,
6951
6952
  /**
6953
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
6954
   */
6955
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
6956
6957
  /**
6958
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
6959
   */
6960
  SPECIAL_AUTHORITY_SLASHES,
6961
6962
  /**
6963
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
6964
   */
6965
  SPECIAL_RELATIVE_OR_AUTHORITY,
6966
6967
  /**
6968
   * @see https://url.spec.whatwg.org/#query-state
6969
   */
6970
  QUERY,
6971
6972
  /**
6973
   * @see https://url.spec.whatwg.org/#path-state
6974
   */
6975
  PATH,
6976
6977
  /**
6978
   * @see https://url.spec.whatwg.org/#path-start-state
6979
   */
6980
  PATH_START,
6981
6982
  /**
6983
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
6984
   */
6985
  OPAQUE_PATH,
6986
6987
  /**
6988
   * @see https://url.spec.whatwg.org/#port-state
6989
   */
6990
  PORT,
6991
};
6992
6993
/**
6994
 * Converts a parser state to its string name for debugging.
6995
 * @param s The state to convert.
6996
 * @return A string representation of the state.
6997
 */
6998
ada_warn_unused std::string to_string(ada::state s);
6999
7000
}  // namespace ada
7001
7002
#endif  // ADA_STATE_H
7003
/* end file include/ada/state.h */
7004
/* begin file include/ada/unicode.h */
7005
/**
7006
 * @file unicode.h
7007
 * @brief Definitions for all unicode specific functions.
7008
 */
7009
#ifndef ADA_UNICODE_H
7010
#define ADA_UNICODE_H
7011
7012
7013
#include <string>
7014
#include <string_view>
7015
#include <optional>
7016
7017
/**
7018
 * Unicode operations. These functions are not part of our public API and may
7019
 * change at any time.
7020
 *
7021
 * @private
7022
 * @namespace ada::unicode
7023
 * @brief Includes the definitions for unicode operations
7024
 */
7025
namespace ada::unicode {
7026
7027
/**
7028
 * @private
7029
 * We receive a UTF-8 string representing a domain name.
7030
 * If the string is percent encoded, we apply percent decoding.
7031
 *
7032
 * Given a domain, we need to identify its labels.
7033
 * They are separated by label-separators:
7034
 *
7035
 * U+002E (.) FULL STOP
7036
 * U+FF0E FULLWIDTH FULL STOP
7037
 * U+3002 IDEOGRAPHIC FULL STOP
7038
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
7039
 *
7040
 * They are all mapped to U+002E.
7041
 *
7042
 * We process each label into a string that should not exceed 63 octets.
7043
 * If the string is already punycode (starts with "xn--"), then we must
7044
 * scan it to look for unallowed code points.
7045
 * Otherwise, if the string is not pure ASCII, we need to transcode it
7046
 * to punycode by following RFC 3454 which requires us to
7047
 * - Map characters  (see section 3),
7048
 * - Normalize (see section 4),
7049
 * - Reject forbidden characters,
7050
 * - Check for right-to-left characters and if so, check all requirements (see
7051
 * section 6),
7052
 * - Optionally reject based on unassigned code points (section 7).
7053
 *
7054
 * The Unicode standard provides a table of code points with a mapping, a list
7055
 * of forbidden code points and so forth. This table is subject to change and
7056
 * will vary based on the implementation. For Unicode 15, the table is at
7057
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
7058
 * If you use ICU, they parse this table and map it to code using a Python
7059
 * script.
7060
 *
7061
 * The resulting strings should not exceed 255 octets according to RFC 1035
7062
 * section 2.3.4. ICU checks for label size and domain size, but these errors
7063
 * are ignored.
7064
 *
7065
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
7066
 *
7067
 */
7068
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
7069
              size_t first_percent);
7070
7071
/**
7072
 * @private
7073
 * Checks if the input has tab or newline characters.
7074
 *
7075
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
7076
 * enough that compilers like GCC can 'autovectorize it'.
7077
 */
7078
ada_really_inline bool has_tabs_or_newline(
7079
    std::string_view user_input) noexcept;
7080
7081
/**
7082
 * @private
7083
 * Checks if the input is a forbidden host code point.
7084
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
7085
 */
7086
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
7087
7088
/**
7089
 * @private
7090
 * Checks if the input contains a forbidden domain code point.
7091
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7092
 */
7093
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
7094
    const char* input, size_t length) noexcept;
7095
7096
/**
7097
 * @private
7098
 * Checks if the input contains a forbidden domain code point in which case
7099
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
7100
 * then the second bit is set to 1.
7101
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7102
 */
7103
ada_really_inline constexpr uint8_t
7104
contains_forbidden_domain_code_point_or_upper(const char* input,
7105
                                              size_t length) noexcept;
7106
7107
/**
7108
 * @private
7109
 * Checks if the input is a forbidden domain code point.
7110
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7111
 */
7112
ada_really_inline constexpr bool is_forbidden_domain_code_point(
7113
    char c) noexcept;
7114
7115
/**
7116
 * @private
7117
 * Checks if the input is alphanumeric, '+', '-' or '.'
7118
 */
7119
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
7120
7121
/**
7122
 * @private
7123
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
7124
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
7125
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
7126
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
7127
 */
7128
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
7129
7130
/**
7131
 * @private
7132
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
7133
 * inclusive.
7134
 */
7135
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
7136
7137
/**
7138
 * @private
7139
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
7140
 * character.
7141
 */
7142
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
7143
7144
/**
7145
 * @private
7146
 * Checks if the input is a C0 control or space character.
7147
 *
7148
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
7149
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
7150
 * SEPARATOR ONE, inclusive.
7151
 */
7152
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
7153
7154
/**
7155
 * @private
7156
 * Checks if the input is a ASCII tab or newline character.
7157
 *
7158
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
7159
 */
7160
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
7161
7162
/**
7163
 * @private
7164
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
7165
 * match for ".%2e", "%2e.", or "%2e%2e".
7166
 */
7167
ada_really_inline constexpr bool is_double_dot_path_segment(
7168
    std::string_view input) noexcept;
7169
7170
/**
7171
 * @private
7172
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
7173
 * match for "%2e".
7174
 */
7175
ada_really_inline constexpr bool is_single_dot_path_segment(
7176
    std::string_view input) noexcept;
7177
7178
/**
7179
 * @private
7180
 * @details ipv4 character might contain 0-9 or a-f character ranges.
7181
 */
7182
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
7183
7184
/**
7185
 * @private
7186
 * @details Convert hex to binary. Caller is responsible to ensure that
7187
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
7188
 */
7189
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
7190
7191
/**
7192
 * @private
7193
 * first_percent should be  = input.find('%')
7194
 *
7195
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
7196
 * @author Node.js
7197
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
7198
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
7199
 */
7200
std::string percent_decode(std::string_view input, size_t first_percent);
7201
7202
/**
7203
 * Decode an application/x-www-form-urlencoded component: map '+' to space,
7204
 * then percent-decode. Single allocation; no intermediate string.
7205
 *
7206
 * @param input A form-urlencoded component (key or value).
7207
 * @return The decoded string.
7208
 * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
7209
 */
7210
std::string form_urlencoded_decode(std::string_view input);
7211
7212
/**
7213
 * @private
7214
 * Returns a percent-encoding string whether percent encoding was needed or not.
7215
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7216
 */
7217
std::string percent_encode(std::string_view input,
7218
                           const uint8_t character_set[]);
7219
/**
7220
 * @private
7221
 * Returns a percent-encoded string version of input, while starting the percent
7222
 * encoding at the provided index.
7223
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7224
 */
7225
std::string percent_encode(std::string_view input,
7226
                           const uint8_t character_set[], size_t index);
7227
/**
7228
 * @private
7229
 * Returns true if percent encoding was needed, in which case, we store
7230
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
7231
 * true, the content is appended to 'out'.
7232
 * If percent encoding is not needed, out is left unchanged.
7233
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7234
 */
7235
template <bool append>
7236
bool percent_encode(std::string_view input, const uint8_t character_set[],
7237
                    std::string& out);
7238
/**
7239
 * @private
7240
 * Returns the index at which percent encoding should start, or (equivalently),
7241
 * the length of the prefix that does not require percent encoding.
7242
 */
7243
ada_really_inline size_t percent_encode_index(std::string_view input,
7244
                                              const uint8_t character_set[]);
7245
/**
7246
 * @private
7247
 * Lowers the string in-place, assuming that the content is ASCII.
7248
 * Return true if the content was ASCII.
7249
 */
7250
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
7251
}  // namespace ada::unicode
7252
7253
#endif  // ADA_UNICODE_H
7254
/* end file include/ada/unicode.h */
7255
/* begin file include/ada/url_base-inl.h */
7256
/**
7257
 * @file url_base-inl.h
7258
 * @brief Inline functions for url base
7259
 */
7260
#ifndef ADA_URL_BASE_INL_H
7261
#define ADA_URL_BASE_INL_H
7262
7263
7264
#include <string>
7265
#if ADA_REGULAR_VISUAL_STUDIO
7266
#include <intrin.h>
7267
#endif  // ADA_REGULAR_VISUAL_STUDIO
7268
7269
namespace ada {
7270
7271
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
7272
1.43M
    const noexcept {
7273
1.43M
  return type != ada::scheme::NOT_SPECIAL;
7274
1.43M
}
7275
7276
2.12k
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
7277
2.12k
  return ada::scheme::get_special_port(type);
7278
2.12k
}
7279
7280
[[nodiscard]] ada_really_inline uint16_t
7281
44.8k
url_base::scheme_default_port() const noexcept {
7282
44.8k
  return scheme::get_special_port(type);
7283
44.8k
}
7284
7285
}  // namespace ada
7286
7287
#endif  // ADA_URL_BASE_INL_H
7288
/* end file include/ada/url_base-inl.h */
7289
/* begin file include/ada/url-inl.h */
7290
/**
7291
 * @file url-inl.h
7292
 * @brief Definitions for the URL
7293
 */
7294
#ifndef ADA_URL_INL_H
7295
#define ADA_URL_INL_H
7296
7297
7298
#include <charconv>
7299
#include <cstring>
7300
#include <optional>
7301
#include <string>
7302
#if ADA_REGULAR_VISUAL_STUDIO
7303
#include <intrin.h>
7304
#endif  // ADA_REGULAR_VISUAL_STUDIO
7305
7306
namespace ada {
7307
0
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7308
0
  return !username.empty() || !password.empty();
7309
0
}
7310
0
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7311
0
  return port.has_value();
7312
0
}
7313
0
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7314
0
  return !host.has_value() || host->empty() || type == ada::scheme::type::FILE;
7315
0
}
7316
0
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7317
0
  if (!host.has_value()) {
7318
0
    return false;
7319
0
  }
7320
0
  return host->empty();
7321
0
}
7322
0
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7323
0
  return host.has_value();
7324
0
}
7325
0
inline std::ostream& operator<<(std::ostream& out, const ada::url& u) {
7326
0
  return out << u.to_string();
7327
0
}
7328
7329
0
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7330
0
  return path.size();
7331
0
}
7332
7333
0
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7334
0
  return path;
7335
0
}
7336
7337
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7338
0
    const {
7339
0
  url_components out{};
7340
0
7341
0
  // protocol ends with ':'. for example: "https:"
7342
0
  out.protocol_end = uint32_t(get_protocol().size());
7343
0
7344
0
  // Trailing index is always the next character of the current one.
7345
0
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7346
0
  size_t running_index = out.protocol_end;
7347
0
7348
0
  if (host.has_value()) {
7349
0
    // 2 characters for "//" and 1 character for starting index
7350
0
    out.host_start = out.protocol_end + 2;
7351
0
7352
0
    if (has_credentials()) {
7353
0
      out.username_end = uint32_t(out.host_start + username.size());
7354
0
7355
0
      out.host_start += uint32_t(username.size());
7356
0
7357
0
      if (!password.empty()) {
7358
0
        out.host_start += uint32_t(password.size() + 1);
7359
0
      }
7360
0
7361
0
      // host_start is the '@' when there are credentials, exactly as
7362
0
      // url_aggregator reports it, so the host itself begins one byte later.
7363
0
      out.host_end = uint32_t(out.host_start + 1 + host->size());
7364
0
    } else {
7365
0
      out.username_end = out.host_start;
7366
0
7367
0
      // Host does not start with "@" if it does not include credentials.
7368
0
      out.host_end = uint32_t(out.host_start + host->size());
7369
0
    }
7370
0
7371
0
    // host_end is one past the host, so it is already the next index.
7372
0
    running_index = out.host_end;
7373
0
  } else {
7374
0
    // Update host start and end date to the same index, since it does not
7375
0
    // exist.
7376
0
    out.host_start = out.protocol_end;
7377
0
    out.host_end = out.host_start;
7378
0
7379
0
    if (!has_opaque_path && path.starts_with("//")) {
7380
0
      // If url's host is null, url does not have an opaque path, url's path's
7381
0
      // size is greater than 1, and url's path[0] is the empty string, then
7382
0
      // append U+002F (/) followed by U+002E (.) to output.
7383
0
      running_index = out.protocol_end + 2;
7384
0
    } else {
7385
0
      running_index = out.protocol_end;
7386
0
    }
7387
0
  }
7388
0
7389
0
  if (port.has_value()) {
7390
0
    out.port = *port;
7391
0
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7392
0
  }
7393
0
7394
0
  out.pathname_start = uint32_t(running_index);
7395
0
7396
0
  running_index += path.size();
7397
0
7398
0
  if (query.has_value()) {
7399
0
    out.search_start = uint32_t(running_index);
7400
0
    running_index += get_search().size();
7401
0
    if (get_search().empty()) {
7402
0
      running_index++;
7403
0
    }
7404
0
  }
7405
0
7406
0
  if (hash.has_value()) {
7407
0
    out.hash_start = uint32_t(running_index);
7408
0
  }
7409
0
7410
0
  return out;
7411
0
}
7412
7413
0
inline void url::update_base_hostname(std::string_view input) { host = input; }
7414
7415
0
inline void url::update_unencoded_base_hash(std::string_view input) {
7416
  // We do the percent encoding
7417
0
  hash = unicode::percent_encode(input,
7418
0
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7419
0
}
7420
7421
inline void url::update_base_search(std::string_view input,
7422
0
                                    const uint8_t query_percent_encode_set[]) {
7423
0
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7424
0
}
7425
7426
0
inline void url::update_base_search(std::optional<std::string>&& input) {
7427
0
  query = std::move(input);
7428
0
}
7429
7430
0
inline void url::update_base_pathname(const std::string_view input) {
7431
0
  path = input;
7432
0
}
7433
7434
0
inline void url::update_base_username(const std::string_view input) {
7435
0
  username = input;
7436
0
}
7437
7438
0
inline void url::update_base_password(const std::string_view input) {
7439
0
  password = input;
7440
0
}
7441
7442
0
inline void url::update_base_port(std::optional<uint16_t> input) {
7443
0
  port = input;
7444
0
}
7445
7446
0
constexpr void url::clear_pathname() { path.clear(); }
7447
7448
0
constexpr void url::clear_search() { query = std::nullopt; }
7449
7450
0
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7451
0
  return hash.has_value();
7452
0
}
7453
7454
0
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7455
0
  return query.has_value();
7456
0
}
7457
7458
0
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7459
7460
0
inline void url::set_scheme(std::string&& new_scheme) noexcept {
7461
0
  type = ada::scheme::get_scheme_type(new_scheme);
7462
  // We only move the 'scheme' if it is non-special.
7463
0
  if (!is_special()) {
7464
0
    non_special_scheme = std::move(new_scheme);
7465
0
  }
7466
0
}
7467
7468
0
constexpr void url::copy_scheme(ada::url&& u) {
7469
0
  non_special_scheme = u.non_special_scheme;
7470
0
  type = u.type;
7471
0
}
7472
7473
0
constexpr void url::copy_scheme(const ada::url& u) {
7474
0
  non_special_scheme = u.non_special_scheme;
7475
0
  type = u.type;
7476
0
}
7477
7478
0
[[nodiscard]] ada_really_inline std::string url::get_href() const {
7479
0
  if (is_special() && host.has_value() && username.empty() &&
7480
0
      password.empty() && !port.has_value()) [[likely]] {
7481
0
    const std::string_view scheme = ada::scheme::details::is_special_list[type];
7482
0
    const size_t host_size = host->size();
7483
0
    const size_t path_size = path.size();
7484
0
    const size_t query_size = query.has_value() ? query->size() : 0;
7485
0
    const size_t hash_size = hash.has_value() ? hash->size() : 0;
7486
0
    const size_t total = scheme.size() + 3 + host_size + path_size +
7487
0
                         (query.has_value() ? query_size + 1 : 0) +
7488
0
                         (hash.has_value() ? hash_size + 1 : 0);
7489
0
    std::string output(total, '\0');
7490
0
    char* p = output.data();
7491
0
    std::memcpy(p, scheme.data(), scheme.size());
7492
0
    p += scheme.size();
7493
0
    p[0] = ':';
7494
0
    p[1] = '/';
7495
0
    p[2] = '/';
7496
0
    p += 3;
7497
0
    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7498
0
    std::memcpy(p, host->data(), host_size);
7499
0
    p += host_size;
7500
0
    std::memcpy(p, path.data(), path_size);
7501
0
    p += path_size;
7502
0
    if (query.has_value()) {
7503
0
      *p++ = '?';
7504
0
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7505
0
      std::memcpy(p, query->data(), query_size);
7506
0
      p += query_size;
7507
0
    }
7508
0
    if (hash.has_value()) {
7509
0
      *p++ = '#';
7510
0
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7511
0
      std::memcpy(p, hash->data(), hash_size);
7512
0
    }
7513
0
    return output;
7514
0
  }
7515
0
7516
0
  std::string output;
7517
0
  output.reserve(get_href_size());
7518
0
7519
0
  if (is_special()) {
7520
0
    output.append(ada::scheme::details::is_special_list[type]);
7521
0
    output += ':';
7522
0
  } else {
7523
0
    output.append(non_special_scheme);
7524
0
    output += ':';
7525
0
  }
7526
0
7527
0
  if (host.has_value()) {
7528
0
    output += '/';
7529
0
    output += '/';
7530
0
    if (has_credentials()) {
7531
0
      output.append(username);
7532
0
      if (!password.empty()) {
7533
0
        output += ':';
7534
0
        output.append(password);
7535
0
      }
7536
0
      output += '@';
7537
0
    }
7538
0
    output.append(*host);
7539
0
    if (port.has_value()) {
7540
0
      output += ':';
7541
0
      char port_buf[5];
7542
0
      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
7543
0
      (void)ec;
7544
0
      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
7545
0
    }
7546
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7547
0
    // If url's host is null, url does not have an opaque path, url's path's
7548
0
    // size is greater than 1, and url's path[0] is the empty string, then
7549
0
    // append U+002F (/) followed by U+002E (.) to output.
7550
0
    output += '/';
7551
0
    output += '.';
7552
0
  }
7553
0
  output.append(path);
7554
0
  if (query.has_value()) {
7555
0
    output += '?';
7556
0
    output.append(*query);
7557
0
  }
7558
0
  if (hash.has_value()) {
7559
0
    output += '#';
7560
0
    output.append(*hash);
7561
0
  }
7562
0
  return output;
7563
0
}
7564
7565
0
[[nodiscard]] inline size_t url::get_href_size() const noexcept {
7566
0
  size_t size = 0;
7567
0
  if (is_special()) {
7568
0
    size += ada::scheme::details::is_special_list[type].size() + 1;
7569
0
  } else {
7570
0
    size += non_special_scheme.size() + 1;
7571
0
  }
7572
0
  if (host.has_value()) {
7573
0
    size += host->size();
7574
0
    size += 2;
7575
0
    if (has_credentials()) {
7576
0
      size += username.size();
7577
0
      if (!password.empty()) {
7578
0
        size += 1 + password.size();
7579
0
      }
7580
0
      size += 1;
7581
0
    }
7582
0
    if (port.has_value()) {
7583
0
      size += 1;
7584
0
      uint16_t p = *port;
7585
0
      size += (p >= 10000)  ? 5
7586
0
              : (p >= 1000) ? 4
7587
0
              : (p >= 100)  ? 3
7588
0
              : (p >= 10)   ? 2
7589
0
                            : 1;
7590
0
    }
7591
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7592
0
    size += 2;
7593
0
  }
7594
0
  size += path.size();
7595
0
  if (query.has_value()) {
7596
0
    size += 1 + query->size();
7597
0
  }
7598
0
  if (hash.has_value()) {
7599
0
    size += 1 + hash->size();
7600
0
  }
7601
0
  return size;
7602
0
}
7603
7604
ada_really_inline size_t url::parse_port(std::string_view view,
7605
0
                                         bool check_trailing_content) noexcept {
7606
0
  ada_log("parse_port('", view, "') ", view.size());
7607
0
  if (!view.empty() && view[0] == '-') {
7608
0
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7609
0
    is_valid = false;
7610
0
    return 0;
7611
0
  }
7612
0
  uint16_t parsed_port{};
7613
0
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7614
0
  if (r.ec == std::errc::result_out_of_range) {
7615
0
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7616
0
    is_valid = false;
7617
0
    return 0;
7618
0
  }
7619
0
  ada_log("parse_port: ", parsed_port);
7620
0
  const auto consumed = size_t(r.ptr - view.data());
7621
0
  ada_log("parse_port: consumed ", consumed);
7622
0
  if (check_trailing_content) {
7623
0
    is_valid &=
7624
0
        (consumed == view.size() || view[consumed] == '/' ||
7625
0
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7626
0
  }
7627
0
  ada_log("parse_port: is_valid = ", is_valid);
7628
0
  if (is_valid) {
7629
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7630
0
    auto default_port = scheme_default_port();
7631
0
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7632
0
                         (default_port != parsed_port);
7633
0
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7634
0
                                                  : std::nullopt;
7635
0
  }
7636
0
  return consumed;
7637
0
}
7638
7639
}  // namespace ada
7640
7641
#endif  // ADA_URL_H
7642
/* end file include/ada/url-inl.h */
7643
/* begin file include/ada/url_components-inl.h */
7644
/**
7645
 * @file url_components.h
7646
 * @brief Declaration for the URL Components
7647
 */
7648
#ifndef ADA_URL_COMPONENTS_INL_H
7649
#define ADA_URL_COMPONENTS_INL_H
7650
7651
7652
namespace ada {
7653
7654
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7655
0
    const noexcept {
7656
0
  /**
7657
0
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7658
0
   *       |     |    |          | ^^^^|       |   |
7659
0
   *       |     |    |          | |   |       |   `----- hash_start
7660
0
   *       |     |    |          | |   |       `--------- search_start
7661
0
   *       |     |    |          | |   `----------------- pathname_start
7662
0
   *       |     |    |          | `--------------------- port
7663
0
   *       |     |    |          `----------------------- host_end
7664
0
   *       |     |    `---------------------------------- host_start
7665
0
   *       |     `--------------------------------------- username_end
7666
0
   *       `--------------------------------------------- protocol_end
7667
0
   */
7668
0
  // These conditions can be made more strict.
7669
0
  if (protocol_end == url_components::omitted) {
7670
0
    return false;
7671
0
  }
7672
0
  uint32_t index = protocol_end;
7673
0
7674
0
  if (username_end == url_components::omitted) {
7675
0
    return false;
7676
0
  }
7677
0
  if (username_end < index) {
7678
0
    return false;
7679
0
  }
7680
0
  index = username_end;
7681
0
7682
0
  if (host_start == url_components::omitted) {
7683
0
    return false;
7684
0
  }
7685
0
  if (host_start < index) {
7686
0
    return false;
7687
0
  }
7688
0
  index = host_start;
7689
0
7690
0
  if (port != url_components::omitted) {
7691
0
    if (port > 0xffff) {
7692
0
      return false;
7693
0
    }
7694
0
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7695
0
    if (index + port_length < index) {
7696
0
      return false;
7697
0
    }
7698
0
    index += port_length;
7699
0
  }
7700
0
7701
0
  if (pathname_start == url_components::omitted) {
7702
0
    return false;
7703
0
  }
7704
0
  if (pathname_start < index) {
7705
0
    return false;
7706
0
  }
7707
0
  index = pathname_start;
7708
0
7709
0
  if (search_start != url_components::omitted) {
7710
0
    if (search_start < index) {
7711
0
      return false;
7712
0
    }
7713
0
    index = search_start;
7714
0
  }
7715
0
7716
0
  if (hash_start != url_components::omitted) {
7717
0
    if (hash_start < index) {
7718
0
      return false;
7719
0
    }
7720
0
  }
7721
0
7722
0
  return true;
7723
0
}
7724
7725
}  // namespace ada
7726
#endif
7727
/* end file include/ada/url_components-inl.h */
7728
/* begin file include/ada/url_aggregator.h */
7729
/**
7730
 * @file url_aggregator.h
7731
 * @brief Declaration for the `ada::url_aggregator` class.
7732
 *
7733
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7734
 * URL using a single buffer with component offsets. This is the default and
7735
 * most memory-efficient URL representation in Ada.
7736
 *
7737
 * @see url.h for an alternative representation using separate strings
7738
 */
7739
#ifndef ADA_URL_AGGREGATOR_H
7740
#define ADA_URL_AGGREGATOR_H
7741
7742
#include <ostream>
7743
#include <string>
7744
#include <string_view>
7745
#include <variant>
7746
7747
7748
namespace ada {
7749
7750
namespace parser {}
7751
7752
/**
7753
 * @brief Memory-efficient URL representation using a single buffer.
7754
 *
7755
 * The `url_aggregator` stores the entire normalized URL in a single string
7756
 * buffer and tracks component boundaries using offsets. This design minimizes
7757
 * memory allocations and is ideal for read-mostly access patterns.
7758
 *
7759
 * Getter methods return `std::string_view` pointing into the internal buffer.
7760
 * These views are lightweight (no allocation) but become invalid if the
7761
 * url_aggregator is modified or destroyed.
7762
 *
7763
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7764
 * when any setter is called. Do not use a getter's result as input to a
7765
 * setter on the same object without copying first.
7766
 *
7767
 * @note This is the default URL type returned by `ada::parse()`.
7768
 *
7769
 * @see url For an alternative using separate std::string instances
7770
 */
7771
struct url_aggregator : url_base {
7772
313k
  url_aggregator() = default;
7773
186k
  url_aggregator(const url_aggregator& u) = default;
7774
195k
  url_aggregator(url_aggregator&& u) noexcept = default;
7775
16.3k
  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
7776
62.6k
  url_aggregator& operator=(const url_aggregator& u) = default;
7777
695k
  ~url_aggregator() override = default;
7778
7779
  /**
7780
   * The setter functions follow the steps defined in the URL Standard.
7781
   *
7782
   * The url_aggregator has a single buffer that contains the entire normalized
7783
   * URL. The various components are represented as offsets into that buffer.
7784
   * When you call get_pathname(), for example, you get a std::string_view that
7785
   * points into that buffer. If the url_aggregator is modified, the buffer may
7786
   * be reallocated, and the std::string_view you obtained earlier may become
7787
   * invalid. In particular, this implies that you cannot modify the URL using
7788
   * a setter function with a std::string_view that points into the
7789
   * url_aggregator E.g., the following is incorrect:
7790
   * url->set_hostname(url->get_pathname()).
7791
   * You must first copy the pathname to a separate string.
7792
   * std::string pathname(url->get_pathname());
7793
   * url->set_hostname(pathname);
7794
   *
7795
   * The caller is responsible for ensuring that the url_aggregator is not
7796
   * modified while any std::string_view obtained from it is in use.
7797
   */
7798
  bool set_href(std::string_view input);
7799
  bool set_host(std::string_view input);
7800
  bool set_hostname(std::string_view input);
7801
  bool set_protocol(std::string_view input);
7802
  bool set_username(std::string_view input);
7803
  bool set_password(std::string_view input);
7804
  bool set_port(std::string_view input);
7805
  bool set_pathname(std::string_view input);
7806
  void set_search(std::string_view input);
7807
  void set_hash(std::string_view input);
7808
7809
  /**
7810
   * Validates whether the hostname is a valid domain according to RFC 1034.
7811
   * @return `true` if the domain is valid, `false` otherwise.
7812
   */
7813
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7814
7815
  /**
7816
   * Returns the URL's origin (scheme + host + port for special URLs).
7817
   * @return A newly allocated string containing the serialized origin.
7818
   * @see https://url.spec.whatwg.org/#concept-url-origin
7819
   */
7820
  [[nodiscard]] std::string get_origin() const override;
7821
7822
  /**
7823
   * Returns the full serialized URL (the href) as a string_view.
7824
   * Does not allocate memory. The returned view becomes invalid if this
7825
   * url_aggregator is modified or destroyed.
7826
   * @return A string_view into the internal buffer.
7827
   * @see https://url.spec.whatwg.org/#dom-url-href
7828
   */
7829
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7830
      ada_lifetime_bound;
7831
7832
  /**
7833
   * Returns the byte length of the serialized URL without allocating a string.
7834
   * @return Size of the href in bytes.
7835
   */
7836
  [[nodiscard]] constexpr size_t get_href_size() const noexcept;
7837
7838
  /**
7839
   * Returns the URL's username component.
7840
   * Does not allocate memory. The returned view becomes invalid if this
7841
   * url_aggregator is modified or destroyed.
7842
   * @return A string_view of the username.
7843
   * @see https://url.spec.whatwg.org/#dom-url-username
7844
   */
7845
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
7846
7847
  /**
7848
   * Returns the URL's password component.
7849
   * Does not allocate memory. The returned view becomes invalid if this
7850
   * url_aggregator is modified or destroyed.
7851
   * @return A string_view of the password.
7852
   * @see https://url.spec.whatwg.org/#dom-url-password
7853
   */
7854
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
7855
7856
  /**
7857
   * Returns the URL's port as a string (e.g., "8080").
7858
   * Does not allocate memory. Returns empty view if no port is set.
7859
   * The returned view becomes invalid if this url_aggregator is modified.
7860
   * @return A string_view of the port.
7861
   * @see https://url.spec.whatwg.org/#dom-url-port
7862
   */
7863
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
7864
7865
  /**
7866
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
7867
   * Does not allocate memory. Returns empty view if no fragment is set.
7868
   * The returned view becomes invalid if this url_aggregator is modified.
7869
   * @return A string_view of the hash.
7870
   * @see https://url.spec.whatwg.org/#dom-url-hash
7871
   */
7872
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
7873
7874
  /**
7875
   * Returns the URL's host and port (e.g., "example.com:8080").
7876
   * Does not allocate memory. Returns empty view if no host is set.
7877
   * The returned view becomes invalid if this url_aggregator is modified.
7878
   * @return A string_view of host:port.
7879
   * @see https://url.spec.whatwg.org/#dom-url-host
7880
   */
7881
  [[nodiscard]] std::string_view get_host() const ada_lifetime_bound;
7882
7883
  /**
7884
   * Returns the URL's hostname (without port).
7885
   * Does not allocate memory. Returns empty view if no host is set.
7886
   * The returned view becomes invalid if this url_aggregator is modified.
7887
   * @return A string_view of the hostname.
7888
   * @see https://url.spec.whatwg.org/#dom-url-hostname
7889
   */
7890
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
7891
7892
  /**
7893
   * Returns the URL's path component.
7894
   * Does not allocate memory. The returned view becomes invalid if this
7895
   * url_aggregator is modified or destroyed.
7896
   * @return A string_view of the pathname.
7897
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7898
   */
7899
  [[nodiscard]] constexpr std::string_view get_pathname() const
7900
      ada_lifetime_bound;
7901
7902
  /**
7903
   * Returns the byte length of the pathname without creating a string.
7904
   * @return Size of the pathname in bytes.
7905
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7906
   */
7907
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
7908
7909
  /**
7910
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
7911
   * Does not allocate memory. Returns empty view if no query is set.
7912
   * The returned view becomes invalid if this url_aggregator is modified.
7913
   * @return A string_view of the search/query.
7914
   * @see https://url.spec.whatwg.org/#dom-url-search
7915
   */
7916
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
7917
7918
  /**
7919
   * Returns the URL's scheme followed by a colon (e.g., "https:").
7920
   * Does not allocate memory. The returned view becomes invalid if this
7921
   * url_aggregator is modified or destroyed.
7922
   * @return A string_view of the protocol.
7923
   * @see https://url.spec.whatwg.org/#dom-url-protocol
7924
   */
7925
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
7926
7927
  /**
7928
   * Checks if the URL has credentials (non-empty username or password).
7929
   * @return `true` if username or password is non-empty, `false` otherwise.
7930
   */
7931
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
7932
      const noexcept;
7933
7934
  /**
7935
   * Returns the URL component offsets for efficient serialization.
7936
   *
7937
   * The components represent byte offsets into the serialized URL:
7938
   * ```
7939
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7940
   *       |     |    |          | ^^^^|       |   |
7941
   *       |     |    |          | |   |       |   `----- hash_start
7942
   *       |     |    |          | |   |       `--------- search_start
7943
   *       |     |    |          | |   `----------------- pathname_start
7944
   *       |     |    |          | `--------------------- port
7945
   *       |     |    |          `----------------------- host_end
7946
   *       |     |    `---------------------------------- host_start
7947
   *       |     `--------------------------------------- username_end
7948
   *       `--------------------------------------------- protocol_end
7949
   * ```
7950
   * @return A constant reference to the url_components struct.
7951
   * @see https://github.com/servo/rust-url
7952
   */
7953
  [[nodiscard]] ada_really_inline const url_components& get_components()
7954
      const noexcept;
7955
7956
  /**
7957
   * Returns a JSON string representation of this URL for debugging.
7958
   * @return A JSON-formatted string with all URL components.
7959
   */
7960
  [[nodiscard]] std::string to_string() const override;
7961
7962
  /**
7963
   * Returns a visual diagram showing component boundaries in the URL.
7964
   * Useful for debugging and understanding URL structure.
7965
   * @return A multi-line string diagram.
7966
   */
7967
  [[nodiscard]] std::string to_diagram() const;
7968
7969
  /**
7970
   * Validates internal consistency of component offsets (for debugging).
7971
   * @return `true` if offsets are consistent, `false` if corrupted.
7972
   */
7973
  [[nodiscard]] constexpr bool validate() const noexcept;
7974
7975
  /**
7976
   * Checks if the URL has an empty hostname (host is set but empty string).
7977
   * @return `true` if host exists but is empty, `false` otherwise.
7978
   */
7979
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
7980
7981
  /**
7982
   * Checks if the URL has a hostname (including empty hostnames).
7983
   * @return `true` if host is present, `false` otherwise.
7984
   */
7985
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
7986
7987
  /**
7988
   * Checks if the URL has a non-empty username.
7989
   * @return `true` if username is non-empty, `false` otherwise.
7990
   */
7991
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
7992
7993
  /**
7994
   * Checks if the URL has a non-empty password.
7995
   * @return `true` if password is non-empty, `false` otherwise.
7996
   */
7997
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
7998
7999
  /**
8000
   * Checks if the URL has a non-default port explicitly specified.
8001
   * @return `true` if a port is present, `false` otherwise.
8002
   */
8003
  [[nodiscard]] constexpr bool has_port() const noexcept;
8004
8005
  /**
8006
   * Checks if the URL has a password component (may be empty).
8007
   * @return `true` if password is present, `false` otherwise.
8008
   */
8009
  [[nodiscard]] constexpr bool has_password() const noexcept;
8010
8011
  /**
8012
   * Checks if the URL has a fragment/hash component.
8013
   * @return `true` if hash is present, `false` otherwise.
8014
   */
8015
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
8016
8017
  /**
8018
   * Checks if the URL has a query/search component.
8019
   * @return `true` if query is present, `false` otherwise.
8020
   */
8021
  [[nodiscard]] constexpr bool has_search() const noexcept override;
8022
8023
  /**
8024
   * Removes the port from the URL.
8025
   */
8026
  inline void clear_port();
8027
8028
  /**
8029
   * Removes the hash/fragment from the URL.
8030
   */
8031
  inline void clear_hash();
8032
8033
  /**
8034
   * Removes the query/search string from the URL.
8035
   */
8036
  inline void clear_search() override;
8037
8038
 private:
8039
  // helper methods
8040
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
8041
      url_aggregator& url);
8042
  // parse_url methods
8043
  friend url_aggregator parser::parse_url<url_aggregator>(
8044
      std::string_view, const url_aggregator*);
8045
8046
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
8047
      std::string_view, const url_aggregator*);
8048
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
8049
      std::string_view, const url_aggregator*);
8050
  template <class result_type>
8051
  friend bool parser::try_parse_simple_absolute(std::string_view, result_type&);
8052
  template <class result_type>
8053
  friend bool parser::finish_simple_absolute_with_port(std::string_view,
8054
                                                       result_type&,
8055
                                                       ada::scheme::type,
8056
                                                       uint32_t, size_t, size_t,
8057
                                                       size_t, bool);
8058
  template <class result_type>
8059
  friend bool parser::try_parse_simple_relative(std::string_view,
8060
                                                const result_type&,
8061
                                                result_type&);
8062
8063
#if ADA_INCLUDE_URL_PATTERN
8064
  // url_pattern methods
8065
  template <url_pattern_regex::regex_concept regex_provider>
8066
  friend tl::expected<url_pattern<regex_provider>, errors>
8067
  parse_url_pattern_impl(
8068
      std::variant<std::string_view, url_pattern_init>&& input,
8069
      const std::string_view* base_url, const url_pattern_options* options);
8070
#endif  // ADA_INCLUDE_URL_PATTERN
8071
8072
  // components is declared before buffer so that the offset fields land
8073
  // close to url_base in memory, improving cache locality for getter calls.
8074
  // Note: exact cache-line placement is implementation- and platform-dependent.
8075
  url_components components{};
8076
  std::string buffer{};
8077
8078
  /**
8079
   * Returns true if neither the search, nor the hash nor the pathname
8080
   * have been set.
8081
   * @return true if the buffer is ready to receive the path.
8082
   */
8083
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
8084
8085
  inline void add_authority_slashes_if_needed();
8086
8087
  /**
8088
   * To optimize performance, you may indicate how much memory to allocate
8089
   * within this instance.
8090
   */
8091
  constexpr void reserve(uint32_t capacity);
8092
8093
  ada_really_inline size_t parse_port(std::string_view view,
8094
                                      bool check_trailing_content) override;
8095
8096
710
  ada_really_inline size_t parse_port(std::string_view view) override {
8097
710
    return this->parse_port(view, false);
8098
710
  }
8099
8100
  /**
8101
   * Return true on success. The 'in_place' parameter indicates whether the
8102
   * the string_view input is pointing in the buffer. When in_place is false,
8103
   * we must nearly always update the buffer.
8104
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
8105
   */
8106
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
8107
8108
  /**
8109
   * Return true on success.
8110
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
8111
   */
8112
  [[nodiscard]] bool parse_ipv6(std::string_view input);
8113
8114
  /**
8115
   * Return true on success.
8116
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
8117
   */
8118
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
8119
8120
  ada_really_inline void parse_path(std::string_view input);
8121
8122
  /**
8123
   * A URL cannot have a username/password/port if its host is null or the empty
8124
   * string, or its scheme is "file".
8125
   */
8126
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
8127
8128
  /**
8129
   * @private
8130
   * Several setters restore a saved copy of the URL when the mutation pushes
8131
   * the buffer past get_max_input_length(). A setter grows the buffer by at
8132
   * most input_len * 3 (worst-case percent-encoding) plus a small constant for
8133
   * delimiters, so this returns false when that upper bound stays within the
8134
   * limit. It does not account for parse-failure rollbacks, which are
8135
   * unrelated to the length limit.
8136
   */
8137
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
8138
8139
  template <bool override_hostname = false>
8140
  bool set_host_or_hostname(std::string_view input);
8141
8142
  ada_really_inline bool parse_host(std::string_view input);
8143
8144
  inline void update_base_authority(std::string_view base_buffer,
8145
                                    const url_components& base);
8146
  inline void update_unencoded_base_hash(std::string_view input);
8147
  inline void update_base_hostname(std::string_view input);
8148
  inline void update_base_search(std::string_view input);
8149
  inline void update_base_search(std::string_view input,
8150
                                 const uint8_t* query_percent_encode_set);
8151
  inline void update_base_pathname(std::string_view input);
8152
  inline void update_base_username(std::string_view input);
8153
  inline void append_base_username(std::string_view input);
8154
  inline void update_base_password(std::string_view input);
8155
  inline void append_base_password(std::string_view input);
8156
  inline void update_base_port(uint32_t input);
8157
  inline void append_base_pathname(std::string_view input);
8158
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
8159
  constexpr void clear_hostname();
8160
  constexpr void clear_password();
8161
  constexpr void clear_pathname() override;
8162
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
8163
  void delete_dash_dot();
8164
  inline void consume_prepared_path(std::string_view input);
8165
  template <bool has_state_override = false>
8166
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
8167
      std::string_view input);
8168
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
8169
                                                std::string_view input);
8170
  [[nodiscard]] constexpr bool has_authority() const noexcept;
8171
  constexpr void set_protocol_as_file();
8172
  inline void set_scheme(std::string_view new_scheme);
8173
  /**
8174
   * Fast function to set the scheme from a view with a colon in the
8175
   * buffer, does not change type.
8176
   */
8177
  inline void set_scheme_from_view_with_colon(
8178
      std::string_view new_scheme_with_colon);
8179
  inline void copy_scheme(const url_aggregator& u);
8180
8181
  inline void update_host_to_base_host(const std::string_view input);
8182
8183
};  // url_aggregator
8184
8185
inline std::ostream& operator<<(std::ostream& out, const url& u);
8186
}  // namespace ada
8187
8188
#endif
8189
/* end file include/ada/url_aggregator.h */
8190
/* begin file include/ada/url_aggregator-inl.h */
8191
/**
8192
 * @file url_aggregator-inl.h
8193
 * @brief Inline functions for url aggregator
8194
 */
8195
#ifndef ADA_URL_AGGREGATOR_INL_H
8196
#define ADA_URL_AGGREGATOR_INL_H
8197
8198
/* begin file include/ada/unicode-inl.h */
8199
/**
8200
 * @file unicode-inl.h
8201
 * @brief Definitions for unicode operations.
8202
 */
8203
#ifndef ADA_UNICODE_INL_H
8204
#define ADA_UNICODE_INL_H
8205
8206
/**
8207
 * Unicode operations. These functions are not part of our public API and may
8208
 * change at any time.
8209
 *
8210
 * private
8211
 * @namespace ada::unicode
8212
 * @brief Includes the declarations for unicode operations
8213
 */
8214
namespace ada::unicode {
8215
ada_really_inline size_t percent_encode_index(const std::string_view input,
8216
109k
                                              const uint8_t character_set[]) {
8217
109k
  const char* data = input.data();
8218
109k
  const size_t size = input.size();
8219
8220
  // Process 8 bytes at a time using unrolled loop
8221
109k
  size_t i = 0;
8222
123k
  for (; i + 8 <= size; i += 8) {
8223
19.8k
    unsigned char chunk[8];
8224
19.8k
    std::memcpy(&chunk, data + i,
8225
19.8k
                8);  // entices compiler to unconditionally process 8 characters
8226
8227
    // Check 8 characters at once
8228
146k
    for (size_t j = 0; j < 8; j++) {
8229
132k
      if (character_sets::bit_at(character_set, chunk[j])) {
8230
6.25k
        return i + j;
8231
6.25k
      }
8232
132k
    }
8233
19.8k
  }
8234
8235
  // Handle remaining bytes
8236
208k
  for (; i < size; i++) {
8237
106k
    if (character_sets::bit_at(character_set, data[i])) {
8238
1.82k
      return i;
8239
1.82k
    }
8240
106k
  }
8241
8242
101k
  return size;
8243
103k
}
8244
}  // namespace ada::unicode
8245
8246
#endif  // ADA_UNICODE_INL_H
8247
/* end file include/ada/unicode-inl.h */
8248
8249
#include <charconv>
8250
#include <cstring>
8251
#include <ostream>
8252
#include <string_view>
8253
8254
namespace ada {
8255
8256
inline void url_aggregator::update_base_authority(
8257
1.43k
    std::string_view base_buffer, const ada::url_components& base) {
8258
1.43k
  std::string_view input = base_buffer.substr(
8259
1.43k
      base.protocol_end, base.host_start - base.protocol_end);
8260
1.43k
  ada_log("url_aggregator::update_base_authority ", input);
8261
8262
1.43k
  bool input_starts_with_dash = input.starts_with("//");
8263
1.43k
  uint32_t diff = components.host_start - components.protocol_end;
8264
8265
1.43k
  buffer.erase(components.protocol_end,
8266
1.43k
               components.host_start - components.protocol_end);
8267
1.43k
  components.username_end = components.protocol_end;
8268
8269
1.43k
  if (input_starts_with_dash) {
8270
1.31k
    input.remove_prefix(2);
8271
1.31k
    diff += 2;  // add "//"
8272
1.31k
    buffer.insert(components.protocol_end, "//");
8273
1.31k
    components.username_end += 2;
8274
1.31k
  }
8275
8276
1.43k
  size_t password_delimiter = input.find(':');
8277
8278
  // Check if input contains both username and password by checking the
8279
  // delimiter: ":" A typical input that contains authority would be "user:pass"
8280
1.43k
  if (password_delimiter != std::string_view::npos) {
8281
    // Insert both username and password
8282
43
    std::string_view username = input.substr(0, password_delimiter);
8283
43
    std::string_view password = input.substr(password_delimiter + 1);
8284
8285
43
    buffer.insert(components.protocol_end + diff, username);
8286
43
    diff += uint32_t(username.size());
8287
43
    buffer.insert(components.protocol_end + diff, ":");
8288
43
    components.username_end = components.protocol_end + diff;
8289
43
    buffer.insert(components.protocol_end + diff + 1, password);
8290
43
    diff += uint32_t(password.size()) + 1;
8291
1.38k
  } else if (!input.empty()) {
8292
    // Insert only username
8293
10
    buffer.insert(components.protocol_end + diff, input);
8294
10
    components.username_end =
8295
10
        components.protocol_end + diff + uint32_t(input.size());
8296
10
    diff += uint32_t(input.size());
8297
10
  }
8298
8299
1.43k
  components.host_start += diff;
8300
8301
1.43k
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
8302
0
    buffer.insert(components.host_start, "@");
8303
0
    diff++;
8304
0
  }
8305
1.43k
  components.host_end += diff;
8306
1.43k
  components.pathname_start += diff;
8307
1.43k
  if (components.search_start != url_components::omitted) {
8308
0
    components.search_start += diff;
8309
0
  }
8310
1.43k
  if (components.hash_start != url_components::omitted) {
8311
0
    components.hash_start += diff;
8312
0
  }
8313
1.43k
}
8314
8315
89.2k
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
8316
89.2k
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
8317
89.2k
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
8318
89.2k
          " bytes] components.hash_start = ", components.hash_start);
8319
89.2k
  ADA_ASSERT_TRUE(validate());
8320
89.2k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8321
89.2k
  if (components.hash_start != url_components::omitted) {
8322
14.4k
    buffer.resize(components.hash_start);
8323
14.4k
  }
8324
89.2k
  components.hash_start = uint32_t(buffer.size());
8325
89.2k
  buffer += "#";
8326
89.2k
  bool encoding_required = unicode::percent_encode<true>(
8327
89.2k
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
8328
  // When encoding_required is false, then buffer is left unchanged, and percent
8329
  // encoding was not deemed required.
8330
89.2k
  if (!encoding_required) {
8331
87.5k
    buffer.append(input);
8332
87.5k
  }
8333
89.2k
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
8334
89.2k
          buffer, "' [", buffer.size(), " bytes]");
8335
89.2k
  ADA_ASSERT_TRUE(validate());
8336
89.2k
}
8337
8338
ada_really_inline uint32_t url_aggregator::replace_and_resize(
8339
492k
    uint32_t start, uint32_t end, std::string_view input) {
8340
492k
  uint32_t current_length = end - start;
8341
492k
  uint32_t input_size = uint32_t(input.size());
8342
492k
  uint32_t new_difference = input_size - current_length;
8343
8344
492k
  if (current_length == 0) {
8345
273k
    buffer.insert(start, input);
8346
273k
  } else if (input_size == current_length) {
8347
83.7k
    if (input_size != 0) {
8348
83.7k
      std::memmove(buffer.data() + start, input.data(), input_size);
8349
83.7k
    }
8350
135k
  } else if (input_size < current_length) {
8351
98.5k
    buffer.erase(start, current_length - input_size);
8352
98.5k
    buffer.replace(start, input_size, input);
8353
98.5k
  } else {
8354
37.0k
    buffer.replace(start, current_length, input.substr(0, current_length));
8355
37.0k
    buffer.insert(start + current_length, input.substr(current_length));
8356
37.0k
  }
8357
8358
492k
  return new_difference;
8359
492k
}
8360
8361
378k
inline void url_aggregator::update_base_hostname(const std::string_view input) {
8362
378k
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
8363
378k
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
8364
378k
  ADA_ASSERT_TRUE(validate());
8365
378k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8366
8367
  // This next line is required for when parsing a URL like `foo://`
8368
378k
  add_authority_slashes_if_needed();
8369
8370
378k
  bool has_credentials = components.protocol_end + 2 < components.host_start;
8371
378k
  uint32_t new_difference =
8372
378k
      replace_and_resize(components.host_start, components.host_end, input);
8373
8374
378k
  if (has_credentials) {
8375
101k
    buffer.insert(components.host_start, "@");
8376
101k
    new_difference++;
8377
101k
  }
8378
378k
  components.host_end += new_difference;
8379
378k
  components.pathname_start += new_difference;
8380
378k
  if (components.search_start != url_components::omitted) {
8381
28.3k
    components.search_start += new_difference;
8382
28.3k
  }
8383
378k
  if (components.hash_start != url_components::omitted) {
8384
27.9k
    components.hash_start += new_difference;
8385
27.9k
  }
8386
378k
  ADA_ASSERT_TRUE(validate());
8387
378k
}
8388
8389
[[nodiscard]] ada_really_inline uint32_t
8390
38.0k
url_aggregator::get_pathname_length() const noexcept {
8391
38.0k
  ada_log("url_aggregator::get_pathname_length");
8392
38.0k
  uint32_t ending_index = uint32_t(buffer.size());
8393
38.0k
  if (components.search_start != url_components::omitted) {
8394
14.8k
    ending_index = components.search_start;
8395
23.2k
  } else if (components.hash_start != url_components::omitted) {
8396
975
    ending_index = components.hash_start;
8397
975
  }
8398
38.0k
  return ending_index - components.pathname_start;
8399
38.0k
}
8400
8401
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
8402
202k
    const noexcept {
8403
202k
  return buffer.size() == components.pathname_start;
8404
202k
}
8405
8406
148
inline void url_aggregator::update_base_search(std::string_view input) {
8407
148
  ada_log("url_aggregator::update_base_search ", input);
8408
148
  ADA_ASSERT_TRUE(validate());
8409
148
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8410
148
  if (input.empty()) {
8411
0
    clear_search();
8412
0
    return;
8413
0
  }
8414
8415
148
  if (input[0] == '?') {
8416
148
    input.remove_prefix(1);
8417
148
  }
8418
8419
148
  if (components.hash_start == url_components::omitted) {
8420
148
    if (components.search_start == url_components::omitted) {
8421
148
      components.search_start = uint32_t(buffer.size());
8422
148
      buffer += "?";
8423
148
    } else {
8424
0
      buffer.resize(components.search_start + 1);
8425
0
    }
8426
8427
148
    buffer.append(input);
8428
148
  } else if (components.search_start != url_components::omitted) {
8429
0
    const uint32_t difference = replace_and_resize(
8430
0
        components.search_start + 1, components.hash_start, input);
8431
0
    components.hash_start += difference;
8432
0
  } else {
8433
0
    components.search_start = components.hash_start;
8434
0
    buffer.insert(components.search_start, input.size() + 1, '?');
8435
0
    if (!input.empty()) {
8436
0
      std::memmove(buffer.data() + components.search_start + 1, input.data(),
8437
0
                   input.size());
8438
0
    }
8439
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8440
0
  }
8441
8442
148
  ADA_ASSERT_TRUE(validate());
8443
148
}
8444
8445
inline void url_aggregator::update_base_search(
8446
89.7k
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8447
89.7k
  ada_log("url_aggregator::update_base_search ", input,
8448
89.7k
          " with encoding parameter ", to_string(), "\n", to_diagram());
8449
89.7k
  ADA_ASSERT_TRUE(validate());
8450
89.7k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8451
8452
89.7k
  if (components.hash_start == url_components::omitted) {
8453
75.3k
    if (components.search_start == url_components::omitted) {
8454
74.2k
      components.search_start = uint32_t(buffer.size());
8455
74.2k
      buffer += "?";
8456
74.2k
    } else {
8457
1.00k
      buffer.resize(components.search_start + 1);
8458
1.00k
    }
8459
8460
75.3k
    bool encoding_required =
8461
75.3k
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8462
    // When encoding_required is false, then buffer is left unchanged, and
8463
    // percent encoding was not deemed required.
8464
75.3k
    if (!encoding_required) {
8465
73.6k
      buffer.append(input);
8466
73.6k
    }
8467
75.3k
  } else {
8468
14.4k
    const size_t idx =
8469
14.4k
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8470
14.4k
    std::string encoded;
8471
14.4k
    std::string_view replacement = input;
8472
14.4k
    if (idx != input.size()) {
8473
349
      encoded =
8474
349
          ada::unicode::percent_encode(input, query_percent_encode_set, idx);
8475
349
      replacement = encoded;
8476
349
    }
8477
8478
14.4k
    if (components.search_start != url_components::omitted) {
8479
13.6k
      const uint32_t difference = replace_and_resize(
8480
13.6k
          components.search_start + 1, components.hash_start, replacement);
8481
13.6k
      components.hash_start += difference;
8482
13.6k
    } else {
8483
774
      components.search_start = components.hash_start;
8484
774
      buffer.insert(components.search_start, replacement.size() + 1, '?');
8485
774
      if (!replacement.empty()) {
8486
769
        std::memmove(buffer.data() + components.search_start + 1,
8487
769
                     replacement.data(), replacement.size());
8488
769
      }
8489
774
      components.hash_start +=
8490
774
          uint32_t(replacement.size() + 1);  // Do not forget `?`
8491
774
    }
8492
14.4k
  }
8493
8494
89.7k
  ADA_ASSERT_TRUE(validate());
8495
89.7k
}
8496
8497
38.0k
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8498
38.0k
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8499
38.0k
          " bytes] \n", to_diagram());
8500
38.0k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8501
38.0k
  ADA_ASSERT_TRUE(validate());
8502
8503
38.0k
  const bool begins_with_dashdash = input.starts_with("//");
8504
38.0k
  if (!begins_with_dashdash && has_dash_dot()) {
8505
    // We must delete the ./
8506
11
    delete_dash_dot();
8507
11
  }
8508
8509
38.0k
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8510
166
      !has_dash_dot()) {
8511
    // If url's host is null, url does not have an opaque path, url's path's
8512
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8513
    // output.
8514
158
    buffer.insert(components.pathname_start, "/.");
8515
158
    components.pathname_start += 2;
8516
158
    if (components.search_start != url_components::omitted) {
8517
15
      components.search_start += 2;
8518
15
    }
8519
158
    if (components.hash_start != url_components::omitted) {
8520
9
      components.hash_start += 2;
8521
9
    }
8522
158
  }
8523
8524
38.0k
  uint32_t difference = replace_and_resize(
8525
38.0k
      components.pathname_start,
8526
38.0k
      components.pathname_start + get_pathname_length(), input);
8527
38.0k
  if (components.search_start != url_components::omitted) {
8528
14.8k
    components.search_start += difference;
8529
14.8k
  }
8530
38.0k
  if (components.hash_start != url_components::omitted) {
8531
14.6k
    components.hash_start += difference;
8532
14.6k
  }
8533
38.0k
  ADA_ASSERT_TRUE(validate());
8534
38.0k
}
8535
8536
2
inline void url_aggregator::append_base_pathname(const std::string_view input) {
8537
2
  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
8538
2
          "\n", to_diagram());
8539
2
  ADA_ASSERT_TRUE(validate());
8540
2
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8541
#if ADA_DEVELOPMENT_CHECKS
8542
  // computing the expected password.
8543
  std::string path_expected(get_pathname());
8544
  path_expected.append(input);
8545
#endif  // ADA_DEVELOPMENT_CHECKS
8546
2
  uint32_t ending_index = uint32_t(buffer.size());
8547
2
  if (components.search_start != url_components::omitted) {
8548
0
    ending_index = components.search_start;
8549
2
  } else if (components.hash_start != url_components::omitted) {
8550
0
    ending_index = components.hash_start;
8551
0
  }
8552
2
  buffer.insert(ending_index, input);
8553
8554
2
  if (components.search_start != url_components::omitted) {
8555
0
    components.search_start += uint32_t(input.size());
8556
0
  }
8557
2
  if (components.hash_start != url_components::omitted) {
8558
0
    components.hash_start += uint32_t(input.size());
8559
0
  }
8560
#if ADA_DEVELOPMENT_CHECKS
8561
  std::string path_after = std::string(get_pathname());
8562
  ADA_ASSERT_EQUAL(
8563
      path_expected, path_after,
8564
      "append_base_pathname problem after inserting " + std::string(input));
8565
#endif  // ADA_DEVELOPMENT_CHECKS
8566
2
  ADA_ASSERT_TRUE(validate());
8567
2
}
8568
8569
48.8k
inline void url_aggregator::update_base_username(const std::string_view input) {
8570
48.8k
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8571
48.8k
          "\n", to_diagram());
8572
48.8k
  ADA_ASSERT_TRUE(validate());
8573
48.8k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8574
8575
48.8k
  add_authority_slashes_if_needed();
8576
8577
48.8k
  bool has_password = has_non_empty_password();
8578
48.8k
  bool host_starts_with_at = buffer.size() > components.host_start &&
8579
48.8k
                             buffer[components.host_start] == '@';
8580
48.8k
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8581
48.8k
                                     components.username_end, input);
8582
8583
48.8k
  components.username_end += diff;
8584
48.8k
  components.host_start += diff;
8585
8586
48.8k
  if (!input.empty() && !host_starts_with_at) {
8587
30.7k
    buffer.insert(components.host_start, "@");
8588
30.7k
    diff++;
8589
30.7k
  } else if (input.empty() && host_starts_with_at && !has_password) {
8590
    // Input is empty, there is no password, and we need to remove "@" from
8591
    // hostname
8592
134
    buffer.erase(components.host_start, 1);
8593
134
    diff--;
8594
134
  }
8595
8596
48.8k
  components.host_end += diff;
8597
48.8k
  components.pathname_start += diff;
8598
48.8k
  if (components.search_start != url_components::omitted) {
8599
14.8k
    components.search_start += diff;
8600
14.8k
  }
8601
48.8k
  if (components.hash_start != url_components::omitted) {
8602
14.6k
    components.hash_start += diff;
8603
14.6k
  }
8604
48.8k
  ADA_ASSERT_TRUE(validate());
8605
48.8k
}
8606
8607
101k
inline void url_aggregator::append_base_username(const std::string_view input) {
8608
101k
  ada_log("url_aggregator::append_base_username ", input);
8609
101k
  ADA_ASSERT_TRUE(validate());
8610
101k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8611
#if ADA_DEVELOPMENT_CHECKS
8612
  // computing the expected password.
8613
  std::string username_expected(get_username());
8614
  username_expected.append(input);
8615
#endif  // ADA_DEVELOPMENT_CHECKS
8616
101k
  add_authority_slashes_if_needed();
8617
8618
  // If input is empty, do nothing.
8619
101k
  if (input.empty()) {
8620
8.83k
    return;
8621
8.83k
  }
8622
8623
92.7k
  uint32_t difference = uint32_t(input.size());
8624
92.7k
  buffer.insert(components.username_end, input);
8625
92.7k
  components.username_end += difference;
8626
92.7k
  components.host_start += difference;
8627
8628
92.7k
  if (buffer[components.host_start] != '@' &&
8629
73.3k
      components.host_start != components.host_end) {
8630
73.3k
    buffer.insert(components.host_start, "@");
8631
73.3k
    difference++;
8632
73.3k
  }
8633
8634
92.7k
  components.host_end += difference;
8635
92.7k
  components.pathname_start += difference;
8636
92.7k
  if (components.search_start != url_components::omitted) {
8637
0
    components.search_start += difference;
8638
0
  }
8639
92.7k
  if (components.hash_start != url_components::omitted) {
8640
0
    components.hash_start += difference;
8641
0
  }
8642
#if ADA_DEVELOPMENT_CHECKS
8643
  std::string username_after(get_username());
8644
  ADA_ASSERT_EQUAL(
8645
      username_expected, username_after,
8646
      "append_base_username problem after inserting " + std::string(input));
8647
#endif  // ADA_DEVELOPMENT_CHECKS
8648
92.7k
  ADA_ASSERT_TRUE(validate());
8649
92.7k
}
8650
8651
2.09k
constexpr void url_aggregator::clear_password() {
8652
2.09k
  ada_log("url_aggregator::clear_password ", to_string());
8653
2.09k
  ADA_ASSERT_TRUE(validate());
8654
2.09k
  if (!has_password()) {
8655
2.00k
    return;
8656
2.00k
  }
8657
8658
99
  uint32_t diff = components.host_start - components.username_end;
8659
99
  buffer.erase(components.username_end, diff);
8660
99
  components.host_start -= diff;
8661
99
  components.host_end -= diff;
8662
99
  components.pathname_start -= diff;
8663
99
  if (components.search_start != url_components::omitted) {
8664
20
    components.search_start -= diff;
8665
20
  }
8666
99
  if (components.hash_start != url_components::omitted) {
8667
28
    components.hash_start -= diff;
8668
28
  }
8669
99
}
8670
8671
46.7k
inline void url_aggregator::update_base_password(const std::string_view input) {
8672
46.7k
  ada_log("url_aggregator::update_base_password ", input);
8673
46.7k
  ADA_ASSERT_TRUE(validate());
8674
46.7k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8675
8676
46.7k
  add_authority_slashes_if_needed();
8677
8678
  // TODO: Optimization opportunity. Merge the following removal functions.
8679
46.7k
  if (input.empty()) {
8680
2.09k
    clear_password();
8681
8682
    // Remove username too, if it is empty.
8683
2.09k
    if (!has_non_empty_username()) {
8684
2.09k
      update_base_username("");
8685
2.09k
    }
8686
8687
2.09k
    return;
8688
2.09k
  }
8689
8690
44.6k
  bool password_exists = has_password();
8691
44.6k
  uint32_t difference = uint32_t(input.size());
8692
8693
44.6k
  if (password_exists) {
8694
13.8k
    difference = replace_and_resize(components.username_end + 1,
8695
13.8k
                                    components.host_start, input);
8696
30.8k
  } else {
8697
30.8k
    buffer.insert(components.username_end, input.size() + 1, ':');
8698
30.8k
    std::memmove(buffer.data() + components.username_end + 1, input.data(),
8699
30.8k
                 input.size());
8700
30.8k
    difference++;
8701
30.8k
  }
8702
44.6k
  components.host_start += difference;
8703
8704
  // The following line is required to add "@" to hostname. When updating
8705
  // password if hostname does not start with "@", it is "update_base_password"s
8706
  // responsibility to set it.
8707
44.6k
  if (buffer[components.host_start] != '@') {
8708
0
    buffer.insert(components.host_start, "@");
8709
0
    difference++;
8710
0
  }
8711
8712
44.6k
  components.host_end += difference;
8713
44.6k
  components.pathname_start += difference;
8714
44.6k
  if (components.search_start != url_components::omitted) {
8715
14.3k
    components.search_start += difference;
8716
14.3k
  }
8717
44.6k
  if (components.hash_start != url_components::omitted) {
8718
14.2k
    components.hash_start += difference;
8719
14.2k
  }
8720
44.6k
  ADA_ASSERT_TRUE(validate());
8721
44.6k
}
8722
8723
84.8k
inline void url_aggregator::append_base_password(const std::string_view input) {
8724
84.8k
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8725
84.8k
          "\n", to_diagram());
8726
84.8k
  ADA_ASSERT_TRUE(validate());
8727
84.8k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8728
#if ADA_DEVELOPMENT_CHECKS
8729
  // computing the expected password.
8730
  std::string password_expected = std::string(get_password());
8731
  password_expected.append(input);
8732
#endif  // ADA_DEVELOPMENT_CHECKS
8733
84.8k
  add_authority_slashes_if_needed();
8734
8735
  // If input is empty, do nothing.
8736
84.8k
  if (input.empty()) {
8737
4.14k
    return;
8738
4.14k
  }
8739
8740
80.6k
  uint32_t difference = uint32_t(input.size());
8741
80.6k
  if (has_password()) {
8742
7.70k
    buffer.insert(components.host_start, input);
8743
72.9k
  } else {
8744
72.9k
    difference++;  // Increment for ":"
8745
72.9k
    buffer.insert(components.username_end, ":");
8746
72.9k
    buffer.insert(components.username_end + 1, input);
8747
72.9k
  }
8748
80.6k
  components.host_start += difference;
8749
8750
  // The following line is required to add "@" to hostname. When updating
8751
  // password if hostname does not start with "@", it is "append_base_password"s
8752
  // responsibility to set it.
8753
80.6k
  if (buffer[components.host_start] != '@') {
8754
328
    buffer.insert(components.host_start, "@");
8755
328
    difference++;
8756
328
  }
8757
8758
80.6k
  components.host_end += difference;
8759
80.6k
  components.pathname_start += difference;
8760
80.6k
  if (components.search_start != url_components::omitted) {
8761
0
    components.search_start += difference;
8762
0
  }
8763
80.6k
  if (components.hash_start != url_components::omitted) {
8764
0
    components.hash_start += difference;
8765
0
  }
8766
#if ADA_DEVELOPMENT_CHECKS
8767
  std::string password_after(get_password());
8768
  ADA_ASSERT_EQUAL(
8769
      password_expected, password_after,
8770
      "append_base_password problem after inserting " + std::string(input));
8771
#endif  // ADA_DEVELOPMENT_CHECKS
8772
80.6k
  ADA_ASSERT_TRUE(validate());
8773
80.6k
}
8774
8775
43.8k
inline void url_aggregator::update_base_port(uint32_t input) {
8776
43.8k
  ada_log("url_aggregator::update_base_port");
8777
43.8k
  ADA_ASSERT_TRUE(validate());
8778
43.8k
  if (input == url_components::omitted) {
8779
1.34k
    clear_port();
8780
1.34k
    return;
8781
1.34k
  }
8782
  // calling std::to_string(input.value()) is unfortunate given that the port
8783
  // value is probably already available as a string.
8784
42.4k
  std::string value = helpers::concat(":", std::to_string(input));
8785
42.4k
  uint32_t difference = uint32_t(value.size());
8786
8787
42.4k
  if (components.port != url_components::omitted) {
8788
73
    difference -= components.pathname_start - components.host_end;
8789
73
    buffer.erase(components.host_end,
8790
73
                 components.pathname_start - components.host_end);
8791
73
  }
8792
8793
42.4k
  buffer.insert(components.host_end, value);
8794
42.4k
  components.pathname_start += difference;
8795
42.4k
  if (components.search_start != url_components::omitted) {
8796
150
    components.search_start += difference;
8797
150
  }
8798
42.4k
  if (components.hash_start != url_components::omitted) {
8799
92
    components.hash_start += difference;
8800
92
  }
8801
42.4k
  components.port = input;
8802
42.4k
  ADA_ASSERT_TRUE(validate());
8803
42.4k
}
8804
8805
68.4k
inline void url_aggregator::clear_port() {
8806
68.4k
  ada_log("url_aggregator::clear_port");
8807
68.4k
  ADA_ASSERT_TRUE(validate());
8808
68.4k
  if (components.port == url_components::omitted) {
8809
53.8k
    return;
8810
53.8k
  }
8811
14.5k
  uint32_t length = components.pathname_start - components.host_end;
8812
14.5k
  buffer.erase(components.host_end, length);
8813
14.5k
  components.pathname_start -= length;
8814
14.5k
  if (components.search_start != url_components::omitted) {
8815
14.4k
    components.search_start -= length;
8816
14.4k
  }
8817
14.5k
  if (components.hash_start != url_components::omitted) {
8818
14.3k
    components.hash_start -= length;
8819
14.3k
  }
8820
14.5k
  components.port = url_components::omitted;
8821
14.5k
  ADA_ASSERT_TRUE(validate());
8822
14.5k
}
8823
8824
1.43k
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8825
1.43k
  ada_log("url_aggregator::retrieve_base_port");
8826
1.43k
  return components.port;
8827
1.43k
}
8828
8829
66.7k
inline void url_aggregator::clear_search() {
8830
66.7k
  ada_log("url_aggregator::clear_search");
8831
66.7k
  ADA_ASSERT_TRUE(validate());
8832
66.7k
  if (components.search_start == url_components::omitted) {
8833
6.90k
    return;
8834
6.90k
  }
8835
8836
59.8k
  if (components.hash_start == url_components::omitted) {
8837
59.8k
    buffer.resize(components.search_start);
8838
59.8k
  } else {
8839
63
    buffer.erase(components.search_start,
8840
63
                 components.hash_start - components.search_start);
8841
63
    components.hash_start = components.search_start;
8842
63
  }
8843
8844
59.8k
  components.search_start = url_components::omitted;
8845
8846
#if ADA_DEVELOPMENT_CHECKS
8847
  ADA_ASSERT_EQUAL(get_search(), "",
8848
                   "search should have been cleared on buffer=" + buffer +
8849
                       " with " + components.to_string() + "\n" + to_diagram());
8850
#endif
8851
59.8k
  ADA_ASSERT_TRUE(validate());
8852
59.8k
}
8853
8854
62.6k
inline void url_aggregator::clear_hash() {
8855
62.6k
  ada_log("url_aggregator::clear_hash");
8856
62.6k
  ADA_ASSERT_TRUE(validate());
8857
62.6k
  if (components.hash_start == url_components::omitted) {
8858
3.14k
    return;
8859
3.14k
  }
8860
59.4k
  buffer.resize(components.hash_start);
8861
59.4k
  components.hash_start = url_components::omitted;
8862
8863
#if ADA_DEVELOPMENT_CHECKS
8864
  ADA_ASSERT_EQUAL(get_hash(), "",
8865
                   "hash should have been cleared on buffer=" + buffer +
8866
                       " with " + components.to_string() + "\n" + to_diagram());
8867
#endif
8868
59.4k
  ADA_ASSERT_TRUE(validate());
8869
59.4k
}
8870
8871
61.8k
constexpr void url_aggregator::clear_pathname() {
8872
61.8k
  ada_log("url_aggregator::clear_pathname");
8873
61.8k
  ADA_ASSERT_TRUE(validate());
8874
61.8k
  uint32_t ending_index = uint32_t(buffer.size());
8875
61.8k
  if (components.search_start != url_components::omitted) {
8876
14.9k
    ending_index = components.search_start;
8877
46.9k
  } else if (components.hash_start != url_components::omitted) {
8878
1.01k
    ending_index = components.hash_start;
8879
1.01k
  }
8880
61.8k
  uint32_t pathname_length = ending_index - components.pathname_start;
8881
61.8k
  buffer.erase(components.pathname_start, pathname_length);
8882
61.8k
  uint32_t difference = pathname_length;
8883
61.8k
  if (components.pathname_start == components.host_end + 2 &&
8884
447
      buffer[components.host_end] == '/' &&
8885
5
      buffer[components.host_end + 1] == '.') {
8886
5
    components.pathname_start -= 2;
8887
5
    buffer.erase(components.host_end, 2);
8888
5
    difference += 2;
8889
5
  }
8890
61.8k
  if (components.search_start != url_components::omitted) {
8891
14.9k
    components.search_start -= difference;
8892
14.9k
  }
8893
61.8k
  if (components.hash_start != url_components::omitted) {
8894
14.6k
    components.hash_start -= difference;
8895
14.6k
  }
8896
61.8k
  ada_log("url_aggregator::clear_pathname completed, running checks...");
8897
#if ADA_DEVELOPMENT_CHECKS
8898
  ADA_ASSERT_EQUAL(get_pathname(), "",
8899
                   "pathname should have been cleared on buffer=" + buffer +
8900
                       " with " + components.to_string() + "\n" + to_diagram());
8901
#endif
8902
61.8k
  ADA_ASSERT_TRUE(validate());
8903
61.8k
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
8904
61.8k
}
8905
8906
947
constexpr void url_aggregator::clear_hostname() {
8907
947
  ada_log("url_aggregator::clear_hostname");
8908
947
  ADA_ASSERT_TRUE(validate());
8909
947
  if (!has_authority()) {
8910
0
    return;
8911
0
  }
8912
947
  ADA_ASSERT_TRUE(has_authority());
8913
8914
947
  uint32_t hostname_length = components.host_end - components.host_start;
8915
947
  uint32_t start = components.host_start;
8916
8917
  // If hostname starts with "@", we should not remove that character.
8918
947
  if (hostname_length > 0 && buffer[start] == '@') {
8919
0
    start++;
8920
0
    hostname_length--;
8921
0
  }
8922
947
  buffer.erase(start, hostname_length);
8923
947
  components.host_end = start;
8924
947
  components.pathname_start -= hostname_length;
8925
947
  if (components.search_start != url_components::omitted) {
8926
169
    components.search_start -= hostname_length;
8927
169
  }
8928
947
  if (components.hash_start != url_components::omitted) {
8929
161
    components.hash_start -= hostname_length;
8930
161
  }
8931
#if ADA_DEVELOPMENT_CHECKS
8932
  ADA_ASSERT_EQUAL(get_hostname(), "",
8933
                   "hostname should have been cleared on buffer=" + buffer +
8934
                       " with " + components.to_string() + "\n" + to_diagram());
8935
#endif
8936
947
  ADA_ASSERT_TRUE(has_authority());
8937
947
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
8938
947
                   "hostname should have been cleared on buffer=" + buffer +
8939
947
                       " with " + components.to_string() + "\n" + to_diagram());
8940
947
  ADA_ASSERT_TRUE(validate());
8941
947
}
8942
8943
67.0k
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
8944
67.0k
  ada_log("url_aggregator::has_hash");
8945
67.0k
  return components.hash_start != url_components::omitted;
8946
67.0k
}
8947
8948
68.7k
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
8949
68.7k
  ada_log("url_aggregator::has_search");
8950
68.7k
  return components.search_start != url_components::omitted;
8951
68.7k
}
8952
8953
69.1k
constexpr bool url_aggregator::has_credentials() const noexcept {
8954
69.1k
  ada_log("url_aggregator::has_credentials");
8955
69.1k
  return has_non_empty_username() || has_non_empty_password();
8956
69.1k
}
8957
8958
189k
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
8959
189k
  ada_log("url_aggregator::cannot_have_credentials_or_port");
8960
189k
  return type == ada::scheme::type::FILE ||
8961
145k
         components.host_start == components.host_end;
8962
189k
}
8963
8964
[[nodiscard]] ada_really_inline const ada::url_components&
8965
67.6k
url_aggregator::get_components() const noexcept {
8966
67.6k
  return components;
8967
67.6k
}
8968
8969
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
8970
854k
    const noexcept {
8971
854k
  ada_log("url_aggregator::has_authority");
8972
  // Performance: instead of doing this potentially expensive check, we could
8973
  // have a boolean in the struct.
8974
854k
  return components.protocol_end + 2 <= components.host_start &&
8975
652k
         buffer[components.protocol_end] == '/' &&
8976
652k
         buffer[components.protocol_end + 1] == '/';
8977
854k
}
8978
8979
660k
inline void ada::url_aggregator::add_authority_slashes_if_needed() {
8980
660k
  ada_log("url_aggregator::add_authority_slashes_if_needed");
8981
660k
  ADA_ASSERT_TRUE(validate());
8982
  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
8983
  // to insert
8984
  // `//` initially to the buffer, since it depends on the hostname existence.
8985
660k
  if (has_authority()) {
8986
461k
    return;
8987
461k
  }
8988
  // Performance: the common case is components.protocol_end == buffer.size()
8989
  // Optimization opportunity: in many cases, the "//" is part of the input and
8990
  // the insert could be fused with another insert.
8991
198k
  buffer.insert(components.protocol_end, "//");
8992
198k
  components.username_end += 2;
8993
198k
  components.host_start += 2;
8994
198k
  components.host_end += 2;
8995
198k
  components.pathname_start += 2;
8996
198k
  if (components.search_start != url_components::omitted) {
8997
62
    components.search_start += 2;
8998
62
  }
8999
198k
  if (components.hash_start != url_components::omitted) {
9000
59
    components.hash_start += 2;
9001
59
  }
9002
198k
  ADA_ASSERT_TRUE(validate());
9003
198k
}
9004
9005
176k
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
9006
176k
  buffer.reserve(capacity);
9007
176k
}
9008
9009
199k
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
9010
199k
  ada_log("url_aggregator::has_non_empty_username");
9011
199k
  return components.protocol_end + 2 < components.username_end;
9012
199k
}
9013
9014
201k
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
9015
201k
  ada_log("url_aggregator::has_non_empty_password");
9016
201k
  return components.host_start > components.username_end;
9017
201k
}
9018
9019
189k
constexpr bool url_aggregator::has_password() const noexcept {
9020
189k
  ada_log("url_aggregator::has_password");
9021
  // This function does not care about the length of the password
9022
189k
  return components.host_start > components.username_end &&
9023
66.2k
         buffer[components.username_end] == ':';
9024
189k
}
9025
9026
62.4k
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
9027
62.4k
  if (!has_hostname()) {
9028
764
    return false;
9029
764
  }
9030
61.7k
  if (components.host_start == components.host_end) {
9031
707
    return true;
9032
707
  }
9033
61.0k
  if (components.host_end > components.host_start + 1) {
9034
47.0k
    return false;
9035
47.0k
  }
9036
13.9k
  return components.username_end != components.host_start;
9037
61.0k
}
9038
9039
192k
constexpr bool url_aggregator::has_hostname() const noexcept {
9040
192k
  return has_authority();
9041
192k
}
9042
9043
66.8k
constexpr bool url_aggregator::has_port() const noexcept {
9044
66.8k
  ada_log("url_aggregator::has_port");
9045
  // A URL cannot have a username/password/port if its host is null or the empty
9046
  // string, or its scheme is "file".
9047
66.8k
  return has_hostname() && components.pathname_start != components.host_end;
9048
66.8k
}
9049
9050
123k
[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
9051
  // If url's host is null, url does not have an opaque path, url's path's size
9052
  // is greater than 1, and url's path[0] is the empty string, then append
9053
  // U+002F (/) followed by U+002E (.) to output.
9054
123k
  ada_log("url_aggregator::has_dash_dot");
9055
#if ADA_DEVELOPMENT_CHECKS
9056
  // If pathname_start and host_end are exactly two characters apart, then we
9057
  // either have a one-digit port such as http://test.com:5?param=1 or else we
9058
  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
9059
  if (components.pathname_start == components.host_end + 2) {
9060
    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
9061
                     buffer[components.host_end + 1] == '.') ||
9062
                    (buffer[components.host_end] == ':' &&
9063
                     checkers::is_digit(buffer[components.host_end + 1])));
9064
  }
9065
  if (components.pathname_start == components.host_end + 2 &&
9066
      buffer[components.host_end] == '/' &&
9067
      buffer[components.host_end + 1] == '.') {
9068
    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
9069
    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
9070
    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
9071
  }
9072
#endif
9073
  // Performance: it should be uncommon for components.pathname_start ==
9074
  // components.host_end + 2 to be true. So we put this check first in the
9075
  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
9076
  // more expensive, but should be uncommon.
9077
123k
  return components.pathname_start == components.host_end + 2 &&
9078
477
         !has_opaque_path && buffer[components.host_end] == '/' &&
9079
71
         buffer[components.host_end + 1] == '.';
9080
123k
}
9081
9082
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
9083
255k
    const noexcept ada_lifetime_bound {
9084
255k
  ada_log("url_aggregator::get_href");
9085
255k
  return buffer;
9086
255k
}
9087
9088
0
[[nodiscard]] constexpr size_t url_aggregator::get_href_size() const noexcept {
9089
0
  return buffer.size();
9090
0
}
9091
9092
ada_really_inline size_t
9093
45.4k
url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
9094
45.4k
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
9095
45.4k
  if (!view.empty() && view[0] == '-') {
9096
19
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
9097
19
    is_valid = false;
9098
19
    return 0;
9099
19
  }
9100
45.4k
  uint16_t parsed_port{};
9101
45.4k
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
9102
45.4k
  if (r.ec == std::errc::result_out_of_range) {
9103
133
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
9104
133
    is_valid = false;
9105
133
    return 0;
9106
133
  }
9107
45.3k
  ada_log("parse_port: ", parsed_port);
9108
45.3k
  const size_t consumed = size_t(r.ptr - view.data());
9109
45.3k
  ada_log("parse_port: consumed ", consumed);
9110
45.3k
  if (check_trailing_content) {
9111
44.6k
    is_valid &=
9112
44.6k
        (consumed == view.size() || view[consumed] == '/' ||
9113
691
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
9114
44.6k
  }
9115
45.3k
  ada_log("parse_port: is_valid = ", is_valid);
9116
45.3k
  if (is_valid) {
9117
44.8k
    ada_log("parse_port", r.ec == std::errc());
9118
    // scheme_default_port can return 0, and we should allow 0 as a base port.
9119
44.8k
    auto default_port = scheme_default_port();
9120
44.8k
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
9121
44.7k
                         (default_port != parsed_port);
9122
44.8k
    if (r.ec == std::errc() && is_port_valid) {
9123
42.4k
      update_base_port(parsed_port);
9124
42.4k
    } else {
9125
2.48k
      clear_port();
9126
2.48k
    }
9127
44.8k
  }
9128
45.3k
  return consumed;
9129
45.4k
}
9130
9131
63.2k
constexpr void url_aggregator::set_protocol_as_file() {
9132
63.2k
  ada_log("url_aggregator::set_protocol_as_file ");
9133
63.2k
  ADA_ASSERT_TRUE(validate());
9134
63.2k
  type = ada::scheme::type::FILE;
9135
  // next line could overflow but unsigned arithmetic has well-defined
9136
  // overflows.
9137
63.2k
  uint32_t new_difference = 5 - components.protocol_end;
9138
9139
63.2k
  if (buffer.empty()) {
9140
544
    buffer.append("file:");
9141
62.7k
  } else {
9142
62.7k
    buffer.erase(0, components.protocol_end);
9143
62.7k
    buffer.insert(0, "file:");
9144
62.7k
  }
9145
63.2k
  components.protocol_end = 5;
9146
9147
  // Update the rest of the components.
9148
63.2k
  components.username_end += new_difference;
9149
63.2k
  components.host_start += new_difference;
9150
63.2k
  components.host_end += new_difference;
9151
63.2k
  components.pathname_start += new_difference;
9152
63.2k
  if (components.search_start != url_components::omitted) {
9153
0
    components.search_start += new_difference;
9154
0
  }
9155
63.2k
  if (components.hash_start != url_components::omitted) {
9156
0
    components.hash_start += new_difference;
9157
0
  }
9158
63.2k
  ADA_ASSERT_TRUE(validate());
9159
63.2k
}
9160
9161
0
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
9162
0
  if (!is_valid) {
9163
0
    return true;
9164
0
  }
9165
0
  if (!components.check_offset_consistency()) {
9166
0
    ada_log("url_aggregator::validate inconsistent components \n",
9167
0
            to_diagram());
9168
0
    return false;
9169
0
  }
9170
0
  // We have a credible components struct, but let us investivate more
9171
0
  // carefully:
9172
0
  /**
9173
0
   * https://user:pass@example.com:1234/foo/bar?baz#quux
9174
0
   *       |     |    |          | ^^^^|       |   |
9175
0
   *       |     |    |          | |   |       |   `----- hash_start
9176
0
   *       |     |    |          | |   |       `--------- search_start
9177
0
   *       |     |    |          | |   `----------------- pathname_start
9178
0
   *       |     |    |          | `--------------------- port
9179
0
   *       |     |    |          `----------------------- host_end
9180
0
   *       |     |    `---------------------------------- host_start
9181
0
   *       |     `--------------------------------------- username_end
9182
0
   *       `--------------------------------------------- protocol_end
9183
0
   */
9184
0
  if (components.protocol_end == url_components::omitted) {
9185
0
    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
9186
0
    return false;
9187
0
  }
9188
0
  if (components.username_end == url_components::omitted) {
9189
0
    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
9190
0
    return false;
9191
0
  }
9192
0
  if (components.host_start == url_components::omitted) {
9193
0
    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
9194
0
    return false;
9195
0
  }
9196
0
  if (components.host_end == url_components::omitted) {
9197
0
    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
9198
0
    return false;
9199
0
  }
9200
0
  if (components.pathname_start == url_components::omitted) {
9201
0
    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
9202
0
    return false;
9203
0
  }
9204
0
9205
0
  if (components.protocol_end > buffer.size()) {
9206
0
    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
9207
0
    return false;
9208
0
  }
9209
0
  if (components.username_end > buffer.size()) {
9210
0
    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
9211
0
    return false;
9212
0
  }
9213
0
  if (components.host_start > buffer.size()) {
9214
0
    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
9215
0
    return false;
9216
0
  }
9217
0
  if (components.host_end > buffer.size()) {
9218
0
    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
9219
0
    return false;
9220
0
  }
9221
0
  if (components.pathname_start > buffer.size()) {
9222
0
    ada_log("url_aggregator::validate pathname_start overflow \n",
9223
0
            to_diagram());
9224
0
    return false;
9225
0
  }
9226
0
9227
0
  if (components.protocol_end > 0) {
9228
0
    if (buffer[components.protocol_end - 1] != ':') {
9229
0
      ada_log(
9230
0
          "url_aggregator::validate missing : at the end of the protocol \n",
9231
0
          to_diagram());
9232
0
      return false;
9233
0
    }
9234
0
  }
9235
0
9236
0
  if (components.username_end != buffer.size() &&
9237
0
      components.username_end > components.protocol_end + 2) {
9238
0
    if (buffer[components.username_end] != ':' &&
9239
0
        buffer[components.username_end] != '@') {
9240
0
      ada_log(
9241
0
          "url_aggregator::validate missing : or @ at the end of the username "
9242
0
          "\n",
9243
0
          to_diagram());
9244
0
      return false;
9245
0
    }
9246
0
  }
9247
0
9248
0
  if (components.host_start != buffer.size()) {
9249
0
    if (components.host_start > components.username_end) {
9250
0
      if (buffer[components.host_start] != '@') {
9251
0
        ada_log(
9252
0
            "url_aggregator::validate missing @ at the end of the password \n",
9253
0
            to_diagram());
9254
0
        return false;
9255
0
      }
9256
0
    } else if (components.host_start == components.username_end &&
9257
0
               components.host_end > components.host_start) {
9258
0
      if (components.host_start == components.protocol_end + 2) {
9259
0
        if (buffer[components.protocol_end] != '/' ||
9260
0
            buffer[components.protocol_end + 1] != '/') {
9261
0
          ada_log(
9262
0
              "url_aggregator::validate missing // between protocol and host "
9263
0
              "\n",
9264
0
              to_diagram());
9265
0
          return false;
9266
0
        }
9267
0
      } else {
9268
0
        if (components.host_start > components.protocol_end &&
9269
0
            buffer[components.host_start] != '@') {
9270
0
          ada_log(
9271
0
              "url_aggregator::validate missing @ at the end of the username "
9272
0
              "\n",
9273
0
              to_diagram());
9274
0
          return false;
9275
0
        }
9276
0
      }
9277
0
    } else {
9278
0
      if (components.host_end != components.host_start) {
9279
0
        ada_log("url_aggregator::validate expected omitted host \n",
9280
0
                to_diagram());
9281
0
        return false;
9282
0
      }
9283
0
    }
9284
0
  }
9285
0
  if (components.host_end != buffer.size() &&
9286
0
      components.pathname_start > components.host_end) {
9287
0
    if (components.pathname_start == components.host_end + 2 &&
9288
0
        buffer[components.host_end] == '/' &&
9289
0
        buffer[components.host_end + 1] == '.') {
9290
0
      if (components.pathname_start + 1 >= buffer.size() ||
9291
0
          buffer[components.pathname_start] != '/' ||
9292
0
          buffer[components.pathname_start + 1] != '/') {
9293
0
        ada_log(
9294
0
            "url_aggregator::validate expected the path to begin with // \n",
9295
0
            to_diagram());
9296
0
        return false;
9297
0
      }
9298
0
    } else if (buffer[components.host_end] != ':') {
9299
0
      ada_log("url_aggregator::validate missing : at the port \n",
9300
0
              to_diagram());
9301
0
      return false;
9302
0
    }
9303
0
  }
9304
0
  if (components.pathname_start != buffer.size() &&
9305
0
      components.pathname_start < components.search_start &&
9306
0
      components.pathname_start < components.hash_start && !has_opaque_path) {
9307
0
    if (buffer[components.pathname_start] != '/') {
9308
0
      ada_log("url_aggregator::validate missing / at the path \n",
9309
0
              to_diagram());
9310
0
      return false;
9311
0
    }
9312
0
  }
9313
0
  if (components.search_start != url_components::omitted) {
9314
0
    if (buffer[components.search_start] != '?') {
9315
0
      ada_log("url_aggregator::validate missing ? at the search \n",
9316
0
              to_diagram());
9317
0
      return false;
9318
0
    }
9319
0
  }
9320
0
  if (components.hash_start != url_components::omitted) {
9321
0
    if (buffer[components.hash_start] != '#') {
9322
0
      ada_log("url_aggregator::validate missing # at the hash \n",
9323
0
              to_diagram());
9324
0
      return false;
9325
0
    }
9326
0
  }
9327
0
9328
0
  return true;
9329
0
}
9330
9331
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() const
9332
155k
    ada_lifetime_bound {
9333
155k
  ada_log("url_aggregator::get_pathname pathname_start = ",
9334
155k
          components.pathname_start, " buffer.size() = ", buffer.size(),
9335
155k
          " components.search_start = ", components.search_start,
9336
155k
          " components.hash_start = ", components.hash_start);
9337
155k
  auto ending_index = uint32_t(buffer.size());
9338
155k
  if (components.search_start != url_components::omitted) {
9339
89.9k
    ending_index = components.search_start;
9340
89.9k
  } else if (components.hash_start != url_components::omitted) {
9341
2.29k
    ending_index = components.hash_start;
9342
2.29k
  }
9343
155k
  return helpers::substring(buffer, components.pathname_start, ending_index);
9344
155k
}
9345
9346
inline std::ostream& operator<<(std::ostream& out,
9347
0
                                const ada::url_aggregator& u) {
9348
0
  return out << u.to_string();
9349
0
}
9350
9351
1.94k
void url_aggregator::update_host_to_base_host(const std::string_view input) {
9352
1.94k
  ada_log("url_aggregator::update_host_to_base_host ", input);
9353
1.94k
  ADA_ASSERT_TRUE(validate());
9354
1.94k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
9355
1.94k
  if (type != ada::scheme::type::FILE) {
9356
    // Let host be the result of host parsing host_view with url is not special.
9357
1.43k
    if (input.empty() && !is_special()) {
9358
128
      if (has_hostname()) {
9359
12
        clear_hostname();
9360
116
      } else if (has_dash_dot()) {
9361
0
        add_authority_slashes_if_needed();
9362
0
        delete_dash_dot();
9363
0
      }
9364
128
      return;
9365
128
    }
9366
1.43k
  }
9367
1.81k
  update_base_hostname(input);
9368
1.81k
  ADA_ASSERT_TRUE(validate());
9369
1.81k
  return;
9370
1.94k
}
9371
}  // namespace ada
9372
9373
#endif  // ADA_URL_AGGREGATOR_INL_H
9374
/* end file include/ada/url_aggregator-inl.h */
9375
/* begin file include/ada/url_search_params.h */
9376
/**
9377
 * @file url_search_params.h
9378
 * @brief URL query string parameter manipulation.
9379
 *
9380
 * This file provides the `url_search_params` class for parsing, manipulating,
9381
 * and serializing URL query strings. It implements the URLSearchParams API
9382
 * from the WHATWG URL Standard.
9383
 *
9384
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9385
 */
9386
#ifndef ADA_URL_SEARCH_PARAMS_H
9387
#define ADA_URL_SEARCH_PARAMS_H
9388
9389
#include <optional>
9390
#include <string>
9391
#include <string_view>
9392
#include <vector>
9393
9394
namespace ada {
9395
9396
/**
9397
 * @brief Iterator types for url_search_params iteration.
9398
 */
9399
enum class url_search_params_iter_type {
9400
  KEYS,    /**< Iterate over parameter keys only */
9401
  VALUES,  /**< Iterate over parameter values only */
9402
  ENTRIES, /**< Iterate over key-value pairs */
9403
};
9404
9405
template <typename T, url_search_params_iter_type Type>
9406
struct url_search_params_iter;
9407
9408
/** Type alias for a key-value pair of string views. */
9409
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
9410
9411
/** Iterator over search parameter keys. */
9412
using url_search_params_keys_iter =
9413
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
9414
/** Iterator over search parameter values. */
9415
using url_search_params_values_iter =
9416
    url_search_params_iter<std::string_view,
9417
                           url_search_params_iter_type::VALUES>;
9418
/** Iterator over search parameter key-value pairs. */
9419
using url_search_params_entries_iter =
9420
    url_search_params_iter<key_value_view_pair,
9421
                           url_search_params_iter_type::ENTRIES>;
9422
9423
/**
9424
 * @brief Class for parsing and manipulating URL query strings.
9425
 *
9426
 * The `url_search_params` class provides methods to parse, modify, and
9427
 * serialize URL query parameters (the part after '?' in a URL). It handles
9428
 * percent-encoding and decoding automatically.
9429
 *
9430
 * All string inputs must be valid UTF-8. The caller is responsible for
9431
 * ensuring UTF-8 validity.
9432
 *
9433
 * Construction and `reset` refuse query strings longer than
9434
 * `get_max_input_length()` (the object is left empty). Individual `append` /
9435
 * `set` calls are not length-capped.
9436
 *
9437
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9438
 */
9439
struct url_search_params {
9440
  url_search_params() = default;
9441
9442
  /**
9443
   * Constructs url_search_params by parsing a query string.
9444
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9445
   *        If longer than `get_max_input_length()`, the object stays empty.
9446
   */
9447
13.5k
  explicit url_search_params(const std::string_view input) {
9448
13.5k
    initialize(input);
9449
13.5k
  }
9450
9451
  url_search_params(const url_search_params& u) = default;
9452
13.5k
  url_search_params(url_search_params&& u) noexcept = default;
9453
  url_search_params& operator=(url_search_params&& u) noexcept = default;
9454
  url_search_params& operator=(const url_search_params& u) = default;
9455
27.1k
  ~url_search_params() = default;
9456
9457
  /**
9458
   * Returns the number of key-value pairs.
9459
   * @return The total count of parameters.
9460
   */
9461
  [[nodiscard]] inline size_t size() const noexcept;
9462
9463
  /**
9464
   * Appends a new key-value pair to the parameter list.
9465
   * @param key The parameter name (must be valid UTF-8).
9466
   * @param value The parameter value (must be valid UTF-8).
9467
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9468
   */
9469
  inline void append(std::string_view key, std::string_view value);
9470
9471
  /**
9472
   * Removes all pairs with the given key.
9473
   * @param key The parameter name to remove.
9474
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9475
   */
9476
  inline void remove(std::string_view key);
9477
9478
  /**
9479
   * Removes all pairs with the given key and value.
9480
   * @param key The parameter name.
9481
   * @param value The parameter value to match.
9482
   */
9483
  inline void remove(std::string_view key, std::string_view value);
9484
9485
  /**
9486
   * Returns the value of the first pair with the given key.
9487
   * @param key The parameter name to search for.
9488
   * @return The value if found, or std::nullopt if not present.
9489
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9490
   */
9491
  inline std::optional<std::string_view> get(std::string_view key);
9492
9493
  /**
9494
   * Returns all values for pairs with the given key.
9495
   * @param key The parameter name to search for.
9496
   * @return A vector of all matching values (may be empty).
9497
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9498
   */
9499
  inline std::vector<std::string> get_all(std::string_view key);
9500
9501
  /**
9502
   * Checks if any pair has the given key.
9503
   * @param key The parameter name to search for.
9504
   * @return `true` if at least one pair has this key.
9505
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9506
   */
9507
  inline bool has(std::string_view key) noexcept;
9508
9509
  /**
9510
   * Checks if any pair matches the given key and value.
9511
   * @param key The parameter name to search for.
9512
   * @param value The parameter value to match.
9513
   * @return `true` if a matching pair exists.
9514
   */
9515
  inline bool has(std::string_view key, std::string_view value) noexcept;
9516
9517
  /**
9518
   * Sets a parameter value, replacing any existing pairs with the same key.
9519
   * @param key The parameter name (must be valid UTF-8).
9520
   * @param value The parameter value (must be valid UTF-8).
9521
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9522
   */
9523
  inline void set(std::string_view key, std::string_view value);
9524
9525
  /**
9526
   * Sorts all key-value pairs by their keys using code unit comparison.
9527
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9528
   */
9529
  inline void sort();
9530
9531
  /**
9532
   * Serializes the parameters to a query string (without leading '?').
9533
   * @return The percent-encoded query string.
9534
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9535
   */
9536
  inline std::string to_string() const;
9537
9538
  /**
9539
   * Returns an iterator over all parameter keys.
9540
   * Keys may repeat if there are duplicate parameters.
9541
   * @return An iterator yielding string_view keys.
9542
   * @note The iterator is invalidated if this object is modified.
9543
   */
9544
  inline url_search_params_keys_iter get_keys();
9545
9546
  /**
9547
   * Returns an iterator over all parameter values.
9548
   * @return An iterator yielding string_view values.
9549
   * @note The iterator is invalidated if this object is modified.
9550
   */
9551
  inline url_search_params_values_iter get_values();
9552
9553
  /**
9554
   * Returns an iterator over all key-value pairs.
9555
   * @return An iterator yielding key-value pair views.
9556
   * @note The iterator is invalidated if this object is modified.
9557
   */
9558
  inline url_search_params_entries_iter get_entries();
9559
9560
  /**
9561
   * C++ style conventional iterator support. const only because we
9562
   * do not really want the params to be modified via the iterator.
9563
   */
9564
0
  inline auto begin() const { return params.begin(); }
9565
0
  inline auto end() const { return params.end(); }
9566
0
  inline auto front() const { return params.front(); }
9567
0
  inline auto back() const { return params.back(); }
9568
0
  inline auto operator[](size_t index) const { return params[index]; }
9569
9570
  /**
9571
   * @private
9572
   * Used to reset the search params to a new input.
9573
   * Used primarily for C API.
9574
   * @param input
9575
   */
9576
  void reset(std::string_view input);
9577
9578
 private:
9579
  typedef std::pair<std::string, std::string> key_value_pair;
9580
  std::vector<key_value_pair> params{};
9581
9582
  /**
9583
   * The init parameter must be valid UTF-8.
9584
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9585
   */
9586
  void initialize(std::string_view init);
9587
9588
  template <typename T, url_search_params_iter_type Type>
9589
  friend struct url_search_params_iter;
9590
};  // url_search_params
9591
9592
/**
9593
 * @brief JavaScript-style iterator for url_search_params.
9594
 *
9595
 * Provides a `next()` method that returns successive values until exhausted.
9596
 * This matches the iterator pattern used in the Web Platform.
9597
 *
9598
 * @tparam T The type of value returned by the iterator.
9599
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9600
 *
9601
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9602
 */
9603
template <typename T, url_search_params_iter_type Type>
9604
struct url_search_params_iter {
9605
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()
9606
  url_search_params_iter(const url_search_params_iter& u) = default;
9607
  url_search_params_iter(url_search_params_iter&& u) noexcept = default;
9608
  url_search_params_iter& operator=(url_search_params_iter&& u) noexcept =
9609
      default;
9610
  url_search_params_iter& operator=(const url_search_params_iter& u) = default;
9611
  ~url_search_params_iter() = default;
9612
9613
  /**
9614
   * Returns the next value in the iteration sequence.
9615
   * @return The next value, or std::nullopt if iteration is complete.
9616
   */
9617
  inline std::optional<T> next();
9618
9619
  /**
9620
   * Checks if more values are available.
9621
   * @return `true` if `next()` will return a value, `false` if exhausted.
9622
   */
9623
  inline bool has_next() const;
9624
9625
 private:
9626
  static url_search_params EMPTY;
9627
40.6k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
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&)
Line
Count
Source
9627
13.5k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
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&)
Line
Count
Source
9627
13.5k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
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&)
Line
Count
Source
9627
13.5k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
9628
9629
  url_search_params& params;
9630
  size_t pos = 0;
9631
9632
  friend struct url_search_params;
9633
};
9634
9635
}  // namespace ada
9636
#endif
9637
/* end file include/ada/url_search_params.h */
9638
/* begin file include/ada/url_search_params-inl.h */
9639
/**
9640
 * @file url_search_params-inl.h
9641
 * @brief Inline declarations for the URL Search Params
9642
 */
9643
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9644
#define ADA_URL_SEARCH_PARAMS_INL_H
9645
9646
9647
#include <algorithm>
9648
#include <cstdint>
9649
#include <optional>
9650
#include <ranges>
9651
#include <string>
9652
#include <string_view>
9653
#include <vector>
9654
9655
namespace ada {
9656
9657
// Declared in implementation.h; used here as a DoS bound on untrusted query
9658
// strings (ada.h includes both headers).
9659
uint32_t get_max_input_length();
9660
9661
// A default, empty url_search_params for use with empty iterators.
9662
template <typename T, ada::url_search_params_iter_type Type>
9663
url_search_params url_search_params_iter<T, Type>::EMPTY;
9664
9665
13.5k
inline void url_search_params::reset(std::string_view input) {
9666
13.5k
  params.clear();
9667
13.5k
  initialize(input);
9668
13.5k
}
9669
9670
27.1k
inline void url_search_params::initialize(std::string_view input) {
9671
27.1k
  if (!input.empty() && input.front() == '?') {
9672
46
    input.remove_prefix(1);
9673
46
  }
9674
27.1k
  if (input.empty()) {
9675
6.80k
    return;
9676
6.80k
  }
9677
  // Refuse overlong query strings (same process-wide cap as URL parsing).
9678
20.2k
  if (input.size() > get_max_input_length()) {
9679
0
    return;
9680
0
  }
9681
9682
20.2k
  params.reserve(size_t(std::count(input.begin(), input.end(), '&')) + 1);
9683
9684
27.1k
  auto process_key_value = [&](const std::string_view current) {
9685
27.1k
    const auto equal = current.find('=');
9686
27.1k
    if (equal == std::string_view::npos) {
9687
24.0k
      params.emplace_back(unicode::form_urlencoded_decode(current), "");
9688
24.0k
    } else {
9689
3.08k
      params.emplace_back(
9690
3.08k
          unicode::form_urlencoded_decode(current.substr(0, equal)),
9691
3.08k
          unicode::form_urlencoded_decode(current.substr(equal + 1)));
9692
3.08k
    }
9693
27.1k
  };
9694
9695
28.1k
  while (!input.empty()) {
9696
28.0k
    const auto ampersand_index = input.find('&');
9697
9698
28.0k
    if (ampersand_index == std::string_view::npos) {
9699
20.2k
      if (!input.empty()) {
9700
20.2k
        process_key_value(input);
9701
20.2k
      }
9702
20.2k
      break;
9703
20.2k
    } else if (ampersand_index != 0) {
9704
6.94k
      process_key_value(input.substr(0, ampersand_index));
9705
6.94k
    }
9706
9707
7.86k
    input.remove_prefix(ampersand_index + 1);
9708
7.86k
  }
9709
20.2k
}
9710
9711
inline void url_search_params::append(const std::string_view key,
9712
13.5k
                                      const std::string_view value) {
9713
13.5k
  params.emplace_back(key, value);
9714
13.5k
}
9715
9716
13.5k
inline size_t url_search_params::size() const noexcept { return params.size(); }
9717
9718
inline std::optional<std::string_view> url_search_params::get(
9719
13.5k
    const std::string_view key) {
9720
13.5k
  auto entry = std::ranges::find_if(
9721
23.3k
      params, [&key](const auto& param) { return param.first == key; });
9722
9723
13.5k
  if (entry == params.end()) {
9724
0
    return std::nullopt;
9725
0
  }
9726
9727
13.5k
  return entry->second;
9728
13.5k
}
9729
9730
inline std::vector<std::string> url_search_params::get_all(
9731
13.5k
    const std::string_view key) {
9732
13.5k
  std::vector<std::string> out{};
9733
9734
23.3k
  for (auto& param : params) {
9735
23.3k
    if (param.first == key) {
9736
13.5k
      out.emplace_back(param.second);
9737
13.5k
    }
9738
23.3k
  }
9739
9740
13.5k
  return out;
9741
13.5k
}
9742
9743
13.5k
inline bool url_search_params::has(const std::string_view key) noexcept {
9744
13.5k
  auto entry = std::ranges::find_if(
9745
23.3k
      params, [&key](const auto& param) { return param.first == key; });
9746
13.5k
  return entry != params.end();
9747
13.5k
}
9748
9749
inline bool url_search_params::has(std::string_view key,
9750
13.5k
                                   std::string_view value) noexcept {
9751
23.3k
  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
9752
23.3k
    return param.first == key && param.second == value;
9753
23.3k
  });
9754
13.5k
  return entry != params.end();
9755
13.5k
}
9756
9757
13.5k
inline std::string url_search_params::to_string() const {
9758
13.5k
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9759
13.5k
  std::string out{};
9760
36.9k
  for (size_t i = 0; i < params.size(); i++) {
9761
23.3k
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9762
23.3k
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9763
9764
    // Performance optimization: Move this inside percent_encode.
9765
23.3k
    std::ranges::replace(key, ' ', '+');
9766
23.3k
    std::ranges::replace(value, ' ', '+');
9767
9768
23.3k
    if (i != 0) {
9769
9.81k
      out += "&";
9770
9.81k
    }
9771
23.3k
    out.append(key);
9772
23.3k
    out += "=";
9773
23.3k
    out.append(value);
9774
23.3k
  }
9775
13.5k
  return out;
9776
13.5k
}
9777
9778
inline void url_search_params::set(const std::string_view key,
9779
13.5k
                                   const std::string_view value) {
9780
33.2k
  const auto find = [&key](const auto& param) { return param.first == key; };
9781
9782
13.5k
  auto it = std::ranges::find_if(params, find);
9783
9784
13.5k
  if (it == params.end()) {
9785
0
    params.emplace_back(key, value);
9786
13.5k
  } else {
9787
13.5k
    it->second = value;
9788
13.5k
    params.erase(std::remove_if(std::next(it), params.end(), find),
9789
13.5k
                 params.end());
9790
13.5k
  }
9791
13.5k
}
9792
9793
13.5k
inline void url_search_params::remove(const std::string_view key) {
9794
13.5k
  std::erase_if(params,
9795
23.3k
                [&key](const auto& param) { return param.first == key; });
9796
13.5k
}
9797
9798
inline void url_search_params::remove(const std::string_view key,
9799
13.5k
                                      const std::string_view value) {
9800
13.5k
  std::erase_if(params, [&key, &value](const auto& param) {
9801
9.81k
    return param.first == key && param.second == value;
9802
9.81k
  });
9803
13.5k
}
9804
9805
13.5k
inline void url_search_params::sort() {
9806
  // Keys are expected to be valid UTF-8, but percent_decode can produce
9807
  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
9808
13.5k
  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
9809
26.9k
                                      const key_value_pair& rhs) {
9810
26.9k
    size_t i = 0, j = 0;
9811
26.9k
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9812
109k
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9813
97.2k
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9814
95.3k
      uint32_t codePoint1 = 0, codePoint2 = 0;
9815
9816
95.3k
      if (low_surrogate1 != 0) {
9817
739
        codePoint1 = low_surrogate1;
9818
739
        low_surrogate1 = 0;
9819
94.5k
      } else {
9820
94.5k
        uint8_t c1 = uint8_t(lhs.first[i]);
9821
94.5k
        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
9822
2.49k
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9823
2.49k
          i += 2;
9824
92.0k
        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
9825
1.56k
          codePoint1 = ((c1 & 0x0F) << 12) |
9826
1.56k
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9827
1.56k
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9828
1.56k
          i += 3;
9829
90.5k
        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
9830
985
          codePoint1 = ((c1 & 0x07) << 18) |
9831
985
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9832
985
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9833
985
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9834
985
          i += 4;
9835
9836
985
          codePoint1 -= 0x10000;
9837
985
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9838
985
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9839
985
          codePoint1 = high_surrogate;
9840
89.5k
        } else {
9841
          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
9842
89.5k
          codePoint1 = c1;
9843
89.5k
          i++;
9844
89.5k
        }
9845
94.5k
      }
9846
9847
95.3k
      if (low_surrogate2 != 0) {
9848
739
        codePoint2 = low_surrogate2;
9849
739
        low_surrogate2 = 0;
9850
94.5k
      } else {
9851
94.5k
        uint8_t c2 = uint8_t(rhs.first[j]);
9852
94.5k
        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
9853
2.39k
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
9854
2.39k
          j += 2;
9855
92.1k
        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
9856
1.59k
          codePoint2 = ((c2 & 0x0F) << 12) |
9857
1.59k
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
9858
1.59k
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
9859
1.59k
          j += 3;
9860
90.5k
        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
9861
1.08k
          codePoint2 = ((c2 & 0x07) << 18) |
9862
1.08k
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
9863
1.08k
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
9864
1.08k
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
9865
1.08k
          j += 4;
9866
1.08k
          codePoint2 -= 0x10000;
9867
1.08k
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
9868
1.08k
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
9869
1.08k
          codePoint2 = high_surrogate;
9870
89.4k
        } else {
9871
          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
9872
89.4k
          codePoint2 = c2;
9873
89.4k
          j++;
9874
89.4k
        }
9875
94.5k
      }
9876
9877
95.3k
      if (codePoint1 != codePoint2) {
9878
12.4k
        return (codePoint1 < codePoint2);
9879
12.4k
      }
9880
95.3k
    }
9881
14.4k
    return (j < rhs.first.size() || low_surrogate2 != 0);
9882
26.9k
  });
9883
13.5k
}
9884
9885
13.5k
inline url_search_params_keys_iter url_search_params::get_keys() {
9886
13.5k
  return url_search_params_keys_iter(*this);
9887
13.5k
}
9888
9889
/**
9890
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9891
 */
9892
13.5k
inline url_search_params_values_iter url_search_params::get_values() {
9893
13.5k
  return url_search_params_values_iter(*this);
9894
13.5k
}
9895
9896
/**
9897
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9898
 */
9899
13.5k
inline url_search_params_entries_iter url_search_params::get_entries() {
9900
13.5k
  return url_search_params_entries_iter(*this);
9901
13.5k
}
9902
9903
template <typename T, url_search_params_iter_type Type>
9904
180k
inline bool url_search_params_iter<T, Type>::has_next() const {
9905
180k
  return pos < params.params.size();
9906
180k
}
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
Line
Count
Source
9904
60.2k
inline bool url_search_params_iter<T, Type>::has_next() const {
9905
60.2k
  return pos < params.params.size();
9906
60.2k
}
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
Line
Count
Source
9904
60.2k
inline bool url_search_params_iter<T, Type>::has_next() const {
9905
60.2k
  return pos < params.params.size();
9906
60.2k
}
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
Line
Count
Source
9904
60.2k
inline bool url_search_params_iter<T, Type>::has_next() const {
9905
60.2k
  return pos < params.params.size();
9906
60.2k
}
9907
9908
template <>
9909
23.3k
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
9910
23.3k
  if (!has_next()) {
9911
0
    return std::nullopt;
9912
0
  }
9913
23.3k
  return params.params[pos++].first;
9914
23.3k
}
9915
9916
template <>
9917
23.3k
inline std::optional<std::string_view> url_search_params_values_iter::next() {
9918
23.3k
  if (!has_next()) {
9919
0
    return std::nullopt;
9920
0
  }
9921
23.3k
  return params.params[pos++].second;
9922
23.3k
}
9923
9924
template <>
9925
inline std::optional<key_value_view_pair>
9926
23.3k
url_search_params_entries_iter::next() {
9927
23.3k
  if (!has_next()) {
9928
0
    return std::nullopt;
9929
0
  }
9930
23.3k
  return params.params[pos++];
9931
23.3k
}
9932
9933
}  // namespace ada
9934
9935
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
9936
/* end file include/ada/url_search_params-inl.h */
9937
9938
/* begin file include/ada/url_pattern-inl.h */
9939
/**
9940
 * @file url_pattern-inl.h
9941
 * @brief Declaration for the URLPattern inline functions.
9942
 */
9943
#ifndef ADA_URL_PATTERN_INL_H
9944
#define ADA_URL_PATTERN_INL_H
9945
9946
9947
#include <algorithm>
9948
#include <string_view>
9949
#include <utility>
9950
9951
#if ADA_INCLUDE_URL_PATTERN
9952
namespace ada {
9953
9954
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
9955
0
  return protocol == other.protocol && username == other.username &&
9956
0
         password == other.password && hostname == other.hostname &&
9957
0
         port == other.port && search == other.search && hash == other.hash &&
9958
0
         pathname == other.pathname;
9959
0
}
9960
9961
inline bool url_pattern_component_result::operator==(
9962
0
    const url_pattern_component_result& other) const {
9963
0
  return input == other.input && groups == other.groups;
9964
0
}
9965
9966
template <url_pattern_regex::regex_concept regex_provider>
9967
url_pattern_component_result
9968
url_pattern_component<regex_provider>::create_component_match_result(
9969
    std::string&& input,
9970
    std::vector<std::optional<std::string>>&& exec_result) {
9971
  // Let result be a new URLPatternComponentResult.
9972
  // Set result["input"] to input.
9973
  // Let groups be a record<USVString, (USVString or undefined)>.
9974
  auto result =
9975
      url_pattern_component_result{.input = std::move(input), .groups = {}};
9976
9977
  // We explicitly start iterating from 0 even though the spec
9978
  // says we should start from 1. This case is handled by the
9979
  // std_regex_provider which removes the full match from index 0.
9980
  // Use min() to guard against potential mismatches between
9981
  // exec_result size and group_name_list size.
9982
  const size_t size = std::min(exec_result.size(), group_name_list.size());
9983
  result.groups.reserve(size);
9984
  for (size_t index = 0; index < size; index++) {
9985
    result.groups.emplace(group_name_list[index],
9986
                          std::move(exec_result[index]));
9987
  }
9988
  return result;
9989
}
9990
9991
template <url_pattern_regex::regex_concept regex_provider>
9992
std::string_view url_pattern<regex_provider>::get_protocol() const
9993
    ada_lifetime_bound {
9994
  // Return this's associated URL pattern's protocol component's pattern string.
9995
  return protocol_component.pattern;
9996
}
9997
template <url_pattern_regex::regex_concept regex_provider>
9998
std::string_view url_pattern<regex_provider>::get_username() const
9999
    ada_lifetime_bound {
10000
  // Return this's associated URL pattern's username component's pattern string.
10001
  return username_component.pattern;
10002
}
10003
template <url_pattern_regex::regex_concept regex_provider>
10004
std::string_view url_pattern<regex_provider>::get_password() const
10005
    ada_lifetime_bound {
10006
  // Return this's associated URL pattern's password component's pattern string.
10007
  return password_component.pattern;
10008
}
10009
template <url_pattern_regex::regex_concept regex_provider>
10010
std::string_view url_pattern<regex_provider>::get_hostname() const
10011
    ada_lifetime_bound {
10012
  // Return this's associated URL pattern's hostname component's pattern string.
10013
  return hostname_component.pattern;
10014
}
10015
template <url_pattern_regex::regex_concept regex_provider>
10016
std::string_view url_pattern<regex_provider>::get_port() const
10017
    ada_lifetime_bound {
10018
  // Return this's associated URL pattern's port component's pattern string.
10019
  return port_component.pattern;
10020
}
10021
template <url_pattern_regex::regex_concept regex_provider>
10022
std::string_view url_pattern<regex_provider>::get_pathname() const
10023
    ada_lifetime_bound {
10024
  // Return this's associated URL pattern's pathname component's pattern string.
10025
  return pathname_component.pattern;
10026
}
10027
template <url_pattern_regex::regex_concept regex_provider>
10028
std::string_view url_pattern<regex_provider>::get_search() const
10029
    ada_lifetime_bound {
10030
  // Return this's associated URL pattern's search component's pattern string.
10031
  return search_component.pattern;
10032
}
10033
template <url_pattern_regex::regex_concept regex_provider>
10034
std::string_view url_pattern<regex_provider>::get_hash() const
10035
    ada_lifetime_bound {
10036
  // Return this's associated URL pattern's hash component's pattern string.
10037
  return hash_component.pattern;
10038
}
10039
template <url_pattern_regex::regex_concept regex_provider>
10040
bool url_pattern<regex_provider>::ignore_case() const {
10041
  return ignore_case_;
10042
}
10043
template <url_pattern_regex::regex_concept regex_provider>
10044
bool url_pattern<regex_provider>::has_regexp_groups() const {
10045
  // If this's associated URL pattern's has regexp groups, then return true.
10046
  return protocol_component.has_regexp_groups ||
10047
         username_component.has_regexp_groups ||
10048
         password_component.has_regexp_groups ||
10049
         hostname_component.has_regexp_groups ||
10050
         port_component.has_regexp_groups ||
10051
         pathname_component.has_regexp_groups ||
10052
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
10053
}
10054
10055
0
inline bool url_pattern_part::is_regexp() const noexcept {
10056
0
  return type == url_pattern_part_type::REGEXP;
10057
0
}
10058
10059
inline std::string_view url_pattern_compile_component_options::get_delimiter()
10060
0
    const {
10061
0
  if (delimiter) {
10062
0
    return {&delimiter.value(), 1};
10063
0
  }
10064
0
  return {};
10065
0
}
10066
10067
inline std::string_view url_pattern_compile_component_options::get_prefix()
10068
0
    const {
10069
0
  if (prefix) {
10070
0
    return {&prefix.value(), 1};
10071
0
  }
10072
0
  return {};
10073
0
}
10074
10075
template <url_pattern_regex::regex_concept regex_provider>
10076
template <url_pattern_encoding_callback F>
10077
tl::expected<url_pattern_component<regex_provider>, errors>
10078
url_pattern_component<regex_provider>::compile(
10079
    std::string_view input, F& encoding_callback,
10080
    url_pattern_compile_component_options& options) {
10081
  ada_log("url_pattern_component::compile input: ", input);
10082
  // Let part list be the result of running parse a pattern string given input,
10083
  // options, and encoding callback.
10084
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
10085
                                                             encoding_callback);
10086
10087
  if (!part_list) {
10088
    ada_log("parse_pattern_string failed");
10089
    return tl::unexpected(part_list.error());
10090
  }
10091
10092
  // Detect pattern type early to potentially skip expensive regex compilation
10093
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
10094
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
10095
10096
  url_pattern_component_type component_type =
10097
      url_pattern_component_type::REGEXP;
10098
  std::string exact_match_value{};
10099
10100
  if (part_list->empty()) {
10101
    component_type = url_pattern_component_type::EMPTY;
10102
  } else if (part_list->size() == 1) {
10103
    const auto& part = (*part_list)[0];
10104
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
10105
        part.modifier == url_pattern_part_modifier::none &&
10106
        !options.ignore_case) {
10107
      component_type = url_pattern_component_type::EXACT_MATCH;
10108
      exact_match_value = part.value;
10109
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
10110
               part.modifier == url_pattern_part_modifier::none &&
10111
               part.prefix.empty() && part.suffix.empty()) {
10112
      component_type = url_pattern_component_type::FULL_WILDCARD;
10113
    }
10114
  }
10115
10116
  // For simple patterns, skip regex generation and compilation entirely
10117
  if (component_type != url_pattern_component_type::REGEXP) {
10118
    auto pattern_string =
10119
        url_pattern_helpers::generate_pattern_string(*part_list, options);
10120
    // For FULL_WILDCARD, we need the group name from
10121
    // generate_regular_expression
10122
    std::vector<std::string> name_list;
10123
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
10124
        !part_list->empty()) {
10125
      name_list.push_back((*part_list)[0].name);
10126
    }
10127
    return url_pattern_component<regex_provider>(
10128
        std::move(pattern_string), typename regex_provider::regex_type{},
10129
        std::move(name_list), has_regexp_groups, component_type,
10130
        std::move(exact_match_value));
10131
  }
10132
10133
  // Generate regex for complex patterns
10134
  auto [regular_expression_string, name_list] =
10135
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
10136
                                                                     options);
10137
  auto pattern_string =
10138
      url_pattern_helpers::generate_pattern_string(*part_list, options);
10139
10140
  std::optional<typename regex_provider::regex_type> regular_expression =
10141
      regex_provider::create_instance(regular_expression_string,
10142
                                      options.ignore_case);
10143
  if (!regular_expression) {
10144
    return tl::unexpected(errors::type_error);
10145
  }
10146
10147
  return url_pattern_component<regex_provider>(
10148
      std::move(pattern_string), std::move(*regular_expression),
10149
      std::move(name_list), has_regexp_groups, component_type,
10150
      std::move(exact_match_value));
10151
}
10152
10153
template <url_pattern_regex::regex_concept regex_provider>
10154
bool url_pattern_component<regex_provider>::fast_test(
10155
    std::string_view input) const noexcept {
10156
  // Fast path for simple patterns - avoid regex evaluation
10157
  // Using if-else for better branch prediction on common cases
10158
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10159
    return true;
10160
  }
10161
  if (type == url_pattern_component_type::EXACT_MATCH) {
10162
    return input == exact_match_value;
10163
  }
10164
  if (type == url_pattern_component_type::EMPTY) {
10165
    return input.empty();
10166
  }
10167
  // type == REGEXP
10168
  return regex_provider::regex_match(input, regexp);
10169
}
10170
10171
template <url_pattern_regex::regex_concept regex_provider>
10172
std::optional<std::vector<std::optional<std::string>>>
10173
url_pattern_component<regex_provider>::fast_match(
10174
    std::string_view input) const {
10175
  // Handle each type directly without redundant checks
10176
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10177
    // FULL_WILDCARD always matches - capture the input (even if empty)
10178
    // If there's no group name, return empty groups
10179
    if (group_name_list.empty()) {
10180
      return std::vector<std::optional<std::string>>{};
10181
    }
10182
    // Capture the matched input (including empty strings)
10183
    return std::vector<std::optional<std::string>>{std::string(input)};
10184
  }
10185
  if (type == url_pattern_component_type::EXACT_MATCH) {
10186
    if (input == exact_match_value) {
10187
      return std::vector<std::optional<std::string>>{};
10188
    }
10189
    return std::nullopt;
10190
  }
10191
  if (type == url_pattern_component_type::EMPTY) {
10192
    if (input.empty()) {
10193
      return std::vector<std::optional<std::string>>{};
10194
    }
10195
    return std::nullopt;
10196
  }
10197
  // type == REGEXP - use regex
10198
  return regex_provider::regex_search(input, regexp);
10199
}
10200
10201
template <url_pattern_regex::regex_concept regex_provider>
10202
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::exec(
10203
    const url_pattern_input& input, const std::string_view* base_url) {
10204
  // Return the result of match given this's associated URL pattern, input, and
10205
  // baseURL if given.
10206
  return match(input, base_url);
10207
}
10208
10209
template <url_pattern_regex::regex_concept regex_provider>
10210
bool url_pattern<regex_provider>::test_components(
10211
    std::string_view protocol, std::string_view username,
10212
    std::string_view password, std::string_view hostname, std::string_view port,
10213
    std::string_view pathname, std::string_view search,
10214
    std::string_view hash) const {
10215
  return protocol_component.fast_test(protocol) &&
10216
         username_component.fast_test(username) &&
10217
         password_component.fast_test(password) &&
10218
         hostname_component.fast_test(hostname) &&
10219
         port_component.fast_test(port) &&
10220
         pathname_component.fast_test(pathname) &&
10221
         search_component.fast_test(search) && hash_component.fast_test(hash);
10222
}
10223
10224
template <url_pattern_regex::regex_concept regex_provider>
10225
result<bool> url_pattern<regex_provider>::test(
10226
    const url_pattern_input& input, const std::string_view* base_url_string) {
10227
  // If input is a URLPatternInit
10228
  if (std::holds_alternative<url_pattern_init>(input)) {
10229
    if (base_url_string) {
10230
      return tl::unexpected(errors::type_error);
10231
    }
10232
10233
    std::string protocol{}, username{}, password{}, hostname{};
10234
    std::string port{}, pathname{}, search{}, hash{};
10235
10236
    auto apply_result = url_pattern_init::process(
10237
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10238
        protocol, username, password, hostname, port, pathname, search, hash);
10239
10240
    if (!apply_result) {
10241
      return false;
10242
    }
10243
10244
    std::string_view search_view = *apply_result->search;
10245
    if (search_view.starts_with("?")) {
10246
      search_view.remove_prefix(1);
10247
    }
10248
10249
    return test_components(*apply_result->protocol, *apply_result->username,
10250
                           *apply_result->password, *apply_result->hostname,
10251
                           *apply_result->port, *apply_result->pathname,
10252
                           search_view, *apply_result->hash);
10253
  }
10254
10255
  // URL string input path
10256
  result<url_aggregator> base_url;
10257
  if (base_url_string) {
10258
    base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10259
    if (!base_url) {
10260
      return false;
10261
    }
10262
  }
10263
10264
  auto url =
10265
      ada::parse<url_aggregator>(std::get<std::string_view>(input),
10266
                                 base_url.has_value() ? &*base_url : nullptr);
10267
  if (!url) {
10268
    return false;
10269
  }
10270
10271
  // Extract components as string_view
10272
  auto protocol_view = url->get_protocol();
10273
  if (protocol_view.ends_with(":")) {
10274
    protocol_view.remove_suffix(1);
10275
  }
10276
10277
  auto search_view = url->get_search();
10278
  if (search_view.starts_with("?")) {
10279
    search_view.remove_prefix(1);
10280
  }
10281
10282
  auto hash_view = url->get_hash();
10283
  if (hash_view.starts_with("#")) {
10284
    hash_view.remove_prefix(1);
10285
  }
10286
10287
  return test_components(protocol_view, url->get_username(),
10288
                         url->get_password(), url->get_hostname(),
10289
                         url->get_port(), url->get_pathname(), search_view,
10290
                         hash_view);
10291
}
10292
10293
template <url_pattern_regex::regex_concept regex_provider>
10294
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::match(
10295
    const url_pattern_input& input, const std::string_view* base_url_string) {
10296
  std::string protocol{};
10297
  std::string username{};
10298
  std::string password{};
10299
  std::string hostname{};
10300
  std::string port{};
10301
  std::string pathname{};
10302
  std::string search{};
10303
  std::string hash{};
10304
10305
  // Let inputs be an empty list.
10306
  // Append input to inputs.
10307
  std::vector inputs{input};
10308
10309
  // If input is a URLPatternInit then:
10310
  if (std::holds_alternative<url_pattern_init>(input)) {
10311
    ada_log(
10312
        "url_pattern::match called with url_pattern_init and base_url_string=",
10313
        base_url_string);
10314
    // If baseURLString was given, throw a TypeError.
10315
    if (base_url_string) {
10316
      ada_log("failed to match because base_url_string was given");
10317
      return tl::unexpected(errors::type_error);
10318
    }
10319
10320
    // Let applyResult be the result of process a URLPatternInit given input,
10321
    // "url", protocol, username, password, hostname, port, pathname, search,
10322
    // and hash.
10323
    auto apply_result = url_pattern_init::process(
10324
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10325
        protocol, username, password, hostname, port, pathname, search, hash);
10326
10327
    // If this throws an exception, catch it, and return null.
10328
    if (!apply_result.has_value()) {
10329
      ada_log("match returned std::nullopt because process threw");
10330
      return std::nullopt;
10331
    }
10332
10333
    // Set protocol to applyResult["protocol"].
10334
    ADA_ASSERT_TRUE(apply_result->protocol.has_value());
10335
    protocol = std::move(apply_result->protocol.value());
10336
10337
    // Set username to applyResult["username"].
10338
    ADA_ASSERT_TRUE(apply_result->username.has_value());
10339
    username = std::move(apply_result->username.value());
10340
10341
    // Set password to applyResult["password"].
10342
    ADA_ASSERT_TRUE(apply_result->password.has_value());
10343
    password = std::move(apply_result->password.value());
10344
10345
    // Set hostname to applyResult["hostname"].
10346
    ADA_ASSERT_TRUE(apply_result->hostname.has_value());
10347
    hostname = std::move(apply_result->hostname.value());
10348
10349
    // Set port to applyResult["port"].
10350
    ADA_ASSERT_TRUE(apply_result->port.has_value());
10351
    port = std::move(apply_result->port.value());
10352
10353
    // Set pathname to applyResult["pathname"].
10354
    ADA_ASSERT_TRUE(apply_result->pathname.has_value());
10355
    pathname = std::move(apply_result->pathname.value());
10356
10357
    // Set search to applyResult["search"].
10358
    ADA_ASSERT_TRUE(apply_result->search.has_value());
10359
    if (apply_result->search->starts_with("?")) {
10360
      search = apply_result->search->substr(1);
10361
    } else {
10362
      search = std::move(apply_result->search.value());
10363
    }
10364
10365
    // Set hash to applyResult["hash"].
10366
    ADA_ASSERT_TRUE(apply_result->hash.has_value());
10367
    ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#"));
10368
    hash = std::move(apply_result->hash.value());
10369
  } else {
10370
    ADA_ASSERT_TRUE(std::holds_alternative<std::string_view>(input));
10371
10372
    // Let baseURL be null.
10373
    result<url_aggregator> base_url;
10374
10375
    // If baseURLString was given, then:
10376
    if (base_url_string) {
10377
      // Let baseURL be the result of parsing baseURLString.
10378
      base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10379
10380
      // If baseURL is failure, return null.
10381
      if (!base_url) {
10382
        ada_log("match returned std::nullopt because failed to parse base_url=",
10383
                *base_url_string);
10384
        return std::nullopt;
10385
      }
10386
10387
      // Append baseURLString to inputs.
10388
      inputs.emplace_back(*base_url_string);
10389
    }
10390
10391
    url_aggregator* base_url_value =
10392
        base_url.has_value() ? &*base_url : nullptr;
10393
10394
    // Set url to the result of parsing input given baseURL.
10395
    auto url = ada::parse<url_aggregator>(std::get<std::string_view>(input),
10396
                                          base_url_value);
10397
10398
    // If url is failure, return null.
10399
    if (!url) {
10400
      ada_log("match returned std::nullopt because url failed");
10401
      return std::nullopt;
10402
    }
10403
10404
    // Set protocol to url's scheme.
10405
    // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':'
10406
    // is removed. Similar work was done on workerd:
10407
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038
10408
    protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1);
10409
    // Set username to url's username.
10410
    username = url->get_username();
10411
    // Set password to url's password.
10412
    password = url->get_password();
10413
    // Set hostname to url's host, serialized, or the empty string if the value
10414
    // is null.
10415
    hostname = url->get_hostname();
10416
    // Set port to url's port, serialized, or the empty string if the value is
10417
    // null.
10418
    port = url->get_port();
10419
    // Set pathname to the result of URL path serializing url.
10420
    pathname = url->get_pathname();
10421
    // Set search to url's query or the empty string if the value is null.
10422
    // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?'
10423
    // is removed. Similar work was done on workerd:
10424
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232
10425
    if (url->has_search()) {
10426
      auto view = url->get_search();
10427
      search = view.starts_with("?") ? url->get_search().substr(1) : view;
10428
    }
10429
    // Set hash to url's fragment or the empty string if the value is null.
10430
    // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is
10431
    // removed. Similar work was done on workerd:
10432
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242
10433
    if (url->has_hash()) {
10434
      auto view = url->get_hash();
10435
      hash = view.starts_with("#") ? url->get_hash().substr(1) : view;
10436
    }
10437
  }
10438
10439
  // Use fast_match which skips regex for simple patterns (EMPTY, EXACT_MATCH,
10440
  // FULL_WILDCARD) and only falls back to regex for complex REGEXP patterns.
10441
10442
  // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol
10443
  // component's regular expression, protocol).
10444
  auto protocol_exec_result = protocol_component.fast_match(protocol);
10445
  if (!protocol_exec_result) {
10446
    return std::nullopt;
10447
  }
10448
10449
  // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username
10450
  // component's regular expression, username).
10451
  auto username_exec_result = username_component.fast_match(username);
10452
  if (!username_exec_result) {
10453
    return std::nullopt;
10454
  }
10455
10456
  // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password
10457
  // component's regular expression, password).
10458
  auto password_exec_result = password_component.fast_match(password);
10459
  if (!password_exec_result) {
10460
    return std::nullopt;
10461
  }
10462
10463
  // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname
10464
  // component's regular expression, hostname).
10465
  auto hostname_exec_result = hostname_component.fast_match(hostname);
10466
  if (!hostname_exec_result) {
10467
    return std::nullopt;
10468
  }
10469
10470
  // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's
10471
  // regular expression, port).
10472
  auto port_exec_result = port_component.fast_match(port);
10473
  if (!port_exec_result) {
10474
    return std::nullopt;
10475
  }
10476
10477
  // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname
10478
  // component's regular expression, pathname).
10479
  auto pathname_exec_result = pathname_component.fast_match(pathname);
10480
  if (!pathname_exec_result) {
10481
    return std::nullopt;
10482
  }
10483
10484
  // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's
10485
  // regular expression, search).
10486
  auto search_exec_result = search_component.fast_match(search);
10487
  if (!search_exec_result) {
10488
    return std::nullopt;
10489
  }
10490
10491
  // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's
10492
  // regular expression, hash).
10493
  auto hash_exec_result = hash_component.fast_match(hash);
10494
  if (!hash_exec_result) {
10495
    return std::nullopt;
10496
  }
10497
10498
  // Let result be a new URLPatternResult.
10499
  auto result = url_pattern_result{};
10500
  // Set result["inputs"] to inputs.
10501
  result.inputs = std::move(inputs);
10502
  // Set result["protocol"] to the result of creating a component match result
10503
  // given urlPattern's protocol component, protocol, and protocolExecResult.
10504
  result.protocol = protocol_component.create_component_match_result(
10505
      std::move(protocol), std::move(*protocol_exec_result));
10506
10507
  // Set result["username"] to the result of creating a component match result
10508
  // given urlPattern's username component, username, and usernameExecResult.
10509
  result.username = username_component.create_component_match_result(
10510
      std::move(username), std::move(*username_exec_result));
10511
10512
  // Set result["password"] to the result of creating a component match result
10513
  // given urlPattern's password component, password, and passwordExecResult.
10514
  result.password = password_component.create_component_match_result(
10515
      std::move(password), std::move(*password_exec_result));
10516
10517
  // Set result["hostname"] to the result of creating a component match result
10518
  // given urlPattern's hostname component, hostname, and hostnameExecResult.
10519
  result.hostname = hostname_component.create_component_match_result(
10520
      std::move(hostname), std::move(*hostname_exec_result));
10521
10522
  // Set result["port"] to the result of creating a component match result given
10523
  // urlPattern's port component, port, and portExecResult.
10524
  result.port = port_component.create_component_match_result(
10525
      std::move(port), std::move(*port_exec_result));
10526
10527
  // Set result["pathname"] to the result of creating a component match result
10528
  // given urlPattern's pathname component, pathname, and pathnameExecResult.
10529
  result.pathname = pathname_component.create_component_match_result(
10530
      std::move(pathname), std::move(*pathname_exec_result));
10531
10532
  // Set result["search"] to the result of creating a component match result
10533
  // given urlPattern's search component, search, and searchExecResult.
10534
  result.search = search_component.create_component_match_result(
10535
      std::move(search), std::move(*search_exec_result));
10536
10537
  // Set result["hash"] to the result of creating a component match result given
10538
  // urlPattern's hash component, hash, and hashExecResult.
10539
  result.hash = hash_component.create_component_match_result(
10540
      std::move(hash), std::move(*hash_exec_result));
10541
10542
  return result;
10543
}
10544
10545
}  // namespace ada
10546
#endif  // ADA_INCLUDE_URL_PATTERN
10547
#endif
10548
/* end file include/ada/url_pattern-inl.h */
10549
/* begin file include/ada/url_pattern_helpers-inl.h */
10550
/**
10551
 * @file url_pattern_helpers-inl.h
10552
 * @brief Declaration for the URLPattern helpers.
10553
 */
10554
#ifndef ADA_URL_PATTERN_HELPERS_INL_H
10555
#define ADA_URL_PATTERN_HELPERS_INL_H
10556
10557
#include <optional>
10558
#include <string_view>
10559
10560
10561
#if ADA_INCLUDE_URL_PATTERN
10562
namespace ada::url_pattern_helpers {
10563
#if defined(ADA_TESTING) || defined(ADA_LOGGING)
10564
0
inline std::string to_string(token_type type) {
10565
0
  switch (type) {
10566
0
    case token_type::INVALID_CHAR:
10567
0
      return "INVALID_CHAR";
10568
0
    case token_type::OPEN:
10569
0
      return "OPEN";
10570
0
    case token_type::CLOSE:
10571
0
      return "CLOSE";
10572
0
    case token_type::REGEXP:
10573
0
      return "REGEXP";
10574
0
    case token_type::NAME:
10575
0
      return "NAME";
10576
0
    case token_type::CHAR:
10577
0
      return "CHAR";
10578
0
    case token_type::ESCAPED_CHAR:
10579
0
      return "ESCAPED_CHAR";
10580
0
    case token_type::OTHER_MODIFIER:
10581
0
      return "OTHER_MODIFIER";
10582
0
    case token_type::ASTERISK:
10583
0
      return "ASTERISK";
10584
0
    case token_type::END:
10585
0
      return "END";
10586
0
    default:
10587
0
      ada::unreachable();
10588
0
  }
10589
0
}
10590
#endif  // defined(ADA_TESTING) || defined(ADA_LOGGING)
10591
10592
template <url_pattern_regex::regex_concept regex_provider>
10593
constexpr void constructor_string_parser<regex_provider>::rewind() {
10594
  // Set parser's token index to parser's component start.
10595
  token_index = component_start;
10596
  // Set parser's token increment to 0.
10597
  token_increment = 0;
10598
}
10599
10600
template <url_pattern_regex::regex_concept regex_provider>
10601
constexpr bool constructor_string_parser<regex_provider>::is_hash_prefix() {
10602
  // Return the result of running is a non-special pattern char given parser,
10603
  // parser's token index and "#".
10604
  return is_non_special_pattern_char(token_index, '#');
10605
}
10606
10607
template <url_pattern_regex::regex_concept regex_provider>
10608
constexpr bool constructor_string_parser<regex_provider>::is_search_prefix() {
10609
  // If result of running is a non-special pattern char given parser, parser's
10610
  // token index and "?" is true, then return true.
10611
  if (is_non_special_pattern_char(token_index, '?')) {
10612
    return true;
10613
  }
10614
10615
  // If parser's token list[parser's token index]'s value is not "?", then
10616
  // return false.
10617
  if (token_list[token_index].value != "?") {
10618
    return false;
10619
  }
10620
10621
  // If previous index is less than 0, then return true.
10622
  if (token_index == 0) return true;
10623
  // Let previous index be parser's token index - 1.
10624
  auto previous_index = token_index - 1;
10625
  // Let previous token be the result of running get a safe token given parser
10626
  // and previous index.
10627
  auto previous_token = get_safe_token(previous_index);
10628
  ADA_ASSERT_TRUE(previous_token);
10629
  // If any of the following are true, then return false:
10630
  // - previous token's type is "name".
10631
  // - previous token's type is "regexp".
10632
  // - previous token's type is "close".
10633
  // - previous token's type is "asterisk".
10634
  return !(previous_token->type == token_type::NAME ||
10635
           previous_token->type == token_type::REGEXP ||
10636
           previous_token->type == token_type::CLOSE ||
10637
           previous_token->type == token_type::ASTERISK);
10638
}
10639
10640
template <url_pattern_regex::regex_concept regex_provider>
10641
constexpr bool
10642
constructor_string_parser<regex_provider>::is_non_special_pattern_char(
10643
    size_t index, uint32_t value) const {
10644
  // Let token be the result of running get a safe token given parser and index.
10645
  auto token = get_safe_token(index);
10646
  ADA_ASSERT_TRUE(token);
10647
10648
  // If token's value is not value, then return false.
10649
  // TODO: Remove this once we make sure get_safe_token returns a non-empty
10650
  // string.
10651
  if (!token->value.empty() &&
10652
      static_cast<uint32_t>(token->value[0]) != value) {
10653
    return false;
10654
  }
10655
10656
  // If any of the following are true:
10657
  // - token's type is "char";
10658
  // - token's type is "escaped-char"; or
10659
  // - token's type is "invalid-char",
10660
  // - then return true.
10661
  return token->type == token_type::CHAR ||
10662
         token->type == token_type::ESCAPED_CHAR ||
10663
         token->type == token_type::INVALID_CHAR;
10664
}
10665
10666
template <url_pattern_regex::regex_concept regex_provider>
10667
constexpr const token*
10668
constructor_string_parser<regex_provider>::get_safe_token(size_t index) const {
10669
  // If index is less than parser's token list's size, then return parser's
10670
  // token list[index].
10671
  if (index < token_list.size()) [[likely]] {
10672
    return &token_list[index];
10673
  }
10674
10675
  // Assert: parser's token list's size is greater than or equal to 1.
10676
  ADA_ASSERT_TRUE(!token_list.empty());
10677
10678
  // Let token be parser's token list[last index].
10679
  // Assert: token's type is "end".
10680
  ADA_ASSERT_TRUE(token_list.back().type == token_type::END);
10681
10682
  // Return token.
10683
  return &token_list.back();
10684
}
10685
10686
template <url_pattern_regex::regex_concept regex_provider>
10687
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10688
    const {
10689
  // If parser's token list[parser's token index]'s type is "open", then return
10690
  // true.
10691
  return token_list[token_index].type == token_type::OPEN;
10692
}
10693
10694
template <url_pattern_regex::regex_concept regex_provider>
10695
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10696
    const {
10697
  // If parser's token list[parser's token index]'s type is "close", then return
10698
  // true.
10699
  return token_list[token_index].type == token_type::CLOSE;
10700
}
10701
10702
template <url_pattern_regex::regex_concept regex_provider>
10703
constexpr bool
10704
constructor_string_parser<regex_provider>::next_is_authority_slashes() const {
10705
  // If the result of running is a non-special pattern char given parser,
10706
  // parser's token index + 1, and "/" is false, then return false.
10707
  if (!is_non_special_pattern_char(token_index + 1, '/')) {
10708
    return false;
10709
  }
10710
  // If the result of running is a non-special pattern char given parser,
10711
  // parser's token index + 2, and "/" is false, then return false.
10712
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10713
    return false;
10714
  }
10715
  return true;
10716
}
10717
10718
template <url_pattern_regex::regex_concept regex_provider>
10719
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10720
    const {
10721
  // Return the result of running is a non-special pattern char given parser,
10722
  // parser's token index, and ":".
10723
  return is_non_special_pattern_char(token_index, ':');
10724
}
10725
10726
template <url_pattern_regex::regex_concept regex_provider>
10727
void constructor_string_parser<regex_provider>::change_state(State new_state,
10728
                                                             size_t skip) {
10729
  // If parser's state is not "init", not "authority", and not "done", then set
10730
  // parser's result[parser's state] to the result of running make a component
10731
  // string given parser.
10732
  if (state != State::INIT && state != State::AUTHORITY &&
10733
      state != State::DONE) {
10734
    auto value = make_component_string();
10735
    // TODO: Simplify this.
10736
    switch (state) {
10737
      case State::PROTOCOL: {
10738
        result.protocol = value;
10739
        break;
10740
      }
10741
      case State::USERNAME: {
10742
        result.username = value;
10743
        break;
10744
      }
10745
      case State::PASSWORD: {
10746
        result.password = value;
10747
        break;
10748
      }
10749
      case State::HOSTNAME: {
10750
        result.hostname = value;
10751
        break;
10752
      }
10753
      case State::PORT: {
10754
        result.port = value;
10755
        break;
10756
      }
10757
      case State::PATHNAME: {
10758
        result.pathname = value;
10759
        break;
10760
      }
10761
      case State::SEARCH: {
10762
        result.search = value;
10763
        break;
10764
      }
10765
      case State::HASH: {
10766
        result.hash = value;
10767
        break;
10768
      }
10769
      default:
10770
        ada::unreachable();
10771
    }
10772
  }
10773
10774
  // If parser's state is not "init" and new state is not "done", then:
10775
  if (state != State::INIT && new_state != State::DONE) {
10776
    // If parser's state is "protocol", "authority", "username", or "password";
10777
    // new state is "port", "pathname", "search", or "hash"; and parser's
10778
    // result["hostname"] does not exist, then set parser's result["hostname"]
10779
    // to the empty string.
10780
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10781
         state == State::USERNAME || state == State::PASSWORD) &&
10782
        (new_state == State::PORT || new_state == State::PATHNAME ||
10783
         new_state == State::SEARCH || new_state == State::HASH) &&
10784
        !result.hostname)
10785
      result.hostname = "";
10786
  }
10787
10788
  // If parser's state is "protocol", "authority", "username", "password",
10789
  // "hostname", or "port"; new state is "search" or "hash"; and parser's
10790
  // result["pathname"] does not exist, then:
10791
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10792
       state == State::USERNAME || state == State::PASSWORD ||
10793
       state == State::HOSTNAME || state == State::PORT) &&
10794
      (new_state == State::SEARCH || new_state == State::HASH) &&
10795
      !result.pathname) {
10796
    if (protocol_matches_a_special_scheme_flag) {
10797
      result.pathname = "/";
10798
    } else {
10799
      // Otherwise, set parser's result["pathname"] to the empty string.
10800
      result.pathname = "";
10801
    }
10802
  }
10803
10804
  // If parser's state is "protocol", "authority", "username", "password",
10805
  // "hostname", "port", or "pathname"; new state is "hash"; and parser's
10806
  // result["search"] does not exist, then set parser's result["search"] to
10807
  // the empty string.
10808
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10809
       state == State::USERNAME || state == State::PASSWORD ||
10810
       state == State::HOSTNAME || state == State::PORT ||
10811
       state == State::PATHNAME) &&
10812
      new_state == State::HASH && !result.search) {
10813
    result.search = "";
10814
  }
10815
10816
  // Set parser's state to new state.
10817
  state = new_state;
10818
  // Increment parser's token index by skip.
10819
  token_index += skip;
10820
  // Set parser's component start to parser's token index.
10821
  component_start = token_index;
10822
  // Set parser's token increment to 0.
10823
  token_increment = 0;
10824
}
10825
10826
template <url_pattern_regex::regex_concept regex_provider>
10827
std::string constructor_string_parser<regex_provider>::make_component_string() {
10828
  // Assert: parser's token index is less than parser's token list's size.
10829
  ADA_ASSERT_TRUE(token_index < token_list.size());
10830
10831
  // Let token be parser's token list[parser's token index].
10832
  // Let end index be token's index.
10833
  const auto end_index = token_list[token_index].index;
10834
  // Let component start token be the result of running get a safe token given
10835
  // parser and parser's component start.
10836
  const auto component_start_token = get_safe_token(component_start);
10837
  ADA_ASSERT_TRUE(component_start_token);
10838
  // Let component start input index be component start token's index.
10839
  const auto component_start_input_index = component_start_token->index;
10840
  // Return the code point substring from component start input index to end
10841
  // index within parser's input.
10842
  return std::string(input.substr(component_start_input_index,
10843
                                  end_index - component_start_input_index));
10844
}
10845
10846
template <url_pattern_regex::regex_concept regex_provider>
10847
constexpr bool
10848
constructor_string_parser<regex_provider>::is_an_identity_terminator() const {
10849
  // Return the result of running is a non-special pattern char given parser,
10850
  // parser's token index, and "@".
10851
  return is_non_special_pattern_char(token_index, '@');
10852
}
10853
10854
template <url_pattern_regex::regex_concept regex_provider>
10855
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
10856
    const {
10857
  // Return the result of running is a non-special pattern char given parser,
10858
  // parser's token index, and "/".
10859
  return is_non_special_pattern_char(token_index, '/');
10860
}
10861
10862
template <url_pattern_regex::regex_concept regex_provider>
10863
constexpr bool constructor_string_parser<regex_provider>::is_password_prefix()
10864
    const {
10865
  // Return the result of running is a non-special pattern char given parser,
10866
  // parser's token index, and ":".
10867
  return is_non_special_pattern_char(token_index, ':');
10868
}
10869
10870
template <url_pattern_regex::regex_concept regex_provider>
10871
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_open()
10872
    const {
10873
  // Return the result of running is a non-special pattern char given parser,
10874
  // parser's token index, and "[".
10875
  return is_non_special_pattern_char(token_index, '[');
10876
}
10877
10878
template <url_pattern_regex::regex_concept regex_provider>
10879
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
10880
    const {
10881
  // Return the result of running is a non-special pattern char given parser,
10882
  // parser's token index, and "]".
10883
  return is_non_special_pattern_char(token_index, ']');
10884
}
10885
10886
template <url_pattern_regex::regex_concept regex_provider>
10887
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
10888
    const {
10889
  // Return the result of running is a non-special pattern char given parser,
10890
  // parser's token index, and ":".
10891
  return is_non_special_pattern_char(token_index, ':');
10892
}
10893
10894
0
constexpr void Tokenizer::get_next_code_point() {
10895
0
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
10896
0
  ADA_ASSERT_TRUE(next_index < input.size());
10897
  // Decode the next UTF-8 code point. If malformed or truncated, mark it as
10898
  // invalid, return the offending byte as the code point, and advance by one
10899
  // to guarantee forward progress.
10900
0
  invalid_code_point = false;
10901
0
  code_point = 0;
10902
0
  size_t number_bytes = 0;
10903
0
  const size_t initial_index = next_index;
10904
0
  unsigned char first_byte = input[next_index];
10905
10906
0
  if ((first_byte & 0x80) == 0) {
10907
    // 1-byte character (ASCII)
10908
0
    next_index++;
10909
0
    code_point = first_byte;
10910
0
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
10911
0
            uint32_t(code_point));
10912
0
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10913
0
            " input.size()=", input.size());
10914
0
    return;
10915
0
  }
10916
0
  ada_log("Tokenizer::get_next_code_point read first byte=",
10917
0
          uint32_t(first_byte));
10918
0
  if ((first_byte & 0xE0) == 0xC0) {
10919
0
    code_point = first_byte & 0x1F;
10920
0
    number_bytes = 2;
10921
0
    ada_log("Tokenizer::get_next_code_point two bytes");
10922
0
  } else if ((first_byte & 0xF0) == 0xE0) {
10923
0
    code_point = first_byte & 0x0F;
10924
0
    number_bytes = 3;
10925
0
    ada_log("Tokenizer::get_next_code_point three bytes");
10926
0
  } else if ((first_byte & 0xF8) == 0xF0) {
10927
0
    code_point = first_byte & 0x07;
10928
0
    number_bytes = 4;
10929
0
    ada_log("Tokenizer::get_next_code_point four bytes");
10930
0
  }
10931
10932
  // Invalid leading bytes that still match a multi-byte prefix.
10933
0
  if ((number_bytes == 2 && first_byte < 0xC2) ||
10934
0
      (number_bytes == 4 && first_byte > 0xF4)) {
10935
0
    invalid_code_point = true;
10936
0
    code_point = first_byte;
10937
0
    next_index = initial_index + 1;
10938
0
    return;
10939
0
  }
10940
10941
  // Invalid leading byte (e.g., continuation byte outside a sequence).
10942
0
  if (number_bytes == 0) {
10943
0
    invalid_code_point = true;
10944
0
    code_point = first_byte;
10945
0
    next_index = initial_index + 1;
10946
0
    return;
10947
0
  }
10948
10949
  // Truncated UTF-8 sequence.
10950
0
  if (number_bytes + next_index > input.size()) {
10951
0
    invalid_code_point = true;
10952
0
    code_point = first_byte;
10953
0
    next_index = initial_index + 1;
10954
0
    return;
10955
0
  }
10956
10957
0
  for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) {
10958
0
    unsigned char byte = input[i];
10959
0
    if ((byte & 0xC0) != 0x80) {
10960
0
      invalid_code_point = true;
10961
0
      code_point = first_byte;
10962
0
      next_index = initial_index + 1;
10963
0
      return;
10964
0
    }
10965
0
    ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte));
10966
0
    code_point = (code_point << 6) | (byte & 0x3F);
10967
0
  }
10968
0
  ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=",
10969
0
          uint32_t(code_point));
10970
0
  ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10971
0
          " input.size()=", input.size());
10972
0
  next_index += number_bytes;
10973
0
}
10974
10975
0
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
10976
0
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
10977
0
          new_index);
10978
  // Set tokenizer's next index to index.
10979
0
  next_index = new_index;
10980
  // Run get the next code point given tokenizer.
10981
0
  get_next_code_point();
10982
0
}
10983
10984
inline void Tokenizer::add_token(token_type type, size_t next_position,
10985
0
                                 size_t value_position, size_t value_length) {
10986
0
  ada_log("Tokenizer::add_token called with type=", to_string(type),
10987
0
          " next_position=", next_position, " value_position=", value_position);
10988
0
  ADA_ASSERT_TRUE(next_position >= value_position);
10989
10990
  // Let token be a new token.
10991
  // Set token's type to type.
10992
  // Set token's index to tokenizer's index.
10993
  // Set token's value to the code point substring from value position with
10994
  // length value length within tokenizer's input.
10995
  // Append token to the back of tokenizer's token list.
10996
0
  token_list.emplace_back(type, index,
10997
0
                          input.substr(value_position, value_length));
10998
  // Set tokenizer's index to next position.
10999
0
  index = next_position;
11000
0
}
11001
11002
inline void Tokenizer::add_token_with_default_length(token_type type,
11003
                                                     size_t next_position,
11004
0
                                                     size_t value_position) {
11005
  // Let computed length be next position - value position.
11006
0
  auto computed_length = next_position - value_position;
11007
  // Run add a token given tokenizer, type, next position, value position, and
11008
  // computed length.
11009
0
  add_token(type, next_position, value_position, computed_length);
11010
0
}
11011
11012
0
inline void Tokenizer::add_token_with_defaults(token_type type) {
11013
0
  ada_log("Tokenizer::add_token_with_defaults called with type=",
11014
0
          to_string(type));
11015
  // Run add a token with default length given tokenizer, type, tokenizer's next
11016
  // index, and tokenizer's index.
11017
0
  add_token_with_default_length(type, next_index, index);
11018
0
}
11019
11020
inline ada_warn_unused std::optional<errors>
11021
Tokenizer::process_tokenizing_error(size_t next_position,
11022
0
                                    size_t value_position) {
11023
  // If tokenizer's policy is "strict", then throw a TypeError.
11024
0
  if (policy == token_policy::strict) {
11025
0
    ada_log("process_tokenizing_error failed with next_position=",
11026
0
            next_position, " value_position=", value_position);
11027
0
    return errors::type_error;
11028
0
  }
11029
  // Assert: tokenizer's policy is "lenient".
11030
0
  ADA_ASSERT_TRUE(policy == token_policy::lenient);
11031
  // Run add a token with default length given tokenizer, "invalid-char", next
11032
  // position, and value position.
11033
0
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
11034
0
                                value_position);
11035
0
  return std::nullopt;
11036
0
}
11037
11038
template <url_pattern_encoding_callback F>
11039
token* url_pattern_parser<F>::try_consume_modifier_token() {
11040
  // Let token be the result of running try to consume a token given parser and
11041
  // "other-modifier".
11042
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
11043
  // If token is not null, then return token.
11044
  if (token) return token;
11045
  // Set token to the result of running try to consume a token given parser and
11046
  // "asterisk".
11047
  // Return token.
11048
  return try_consume_token(token_type::ASTERISK);
11049
}
11050
11051
template <url_pattern_encoding_callback F>
11052
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
11053
    const token* name_token) {
11054
  // Let token be the result of running try to consume a token given parser and
11055
  // "regexp".
11056
  auto token = try_consume_token(token_type::REGEXP);
11057
  // If name token is null and token is null, then set token to the result of
11058
  // running try to consume a token given parser and "asterisk".
11059
  if (!name_token && !token) {
11060
    token = try_consume_token(token_type::ASTERISK);
11061
  }
11062
  // Return token.
11063
  return token;
11064
}
11065
11066
template <url_pattern_encoding_callback F>
11067
token* url_pattern_parser<F>::try_consume_token(token_type type) {
11068
  ada_log("url_pattern_parser::try_consume_token called with type=",
11069
          to_string(type));
11070
  // Assert: parser's index is less than parser's token list size.
11071
  ADA_ASSERT_TRUE(index < tokens.size());
11072
  // Let next token be parser's token list[parser's index].
11073
  auto& next_token = tokens[index];
11074
  // If next token's type is not type return null.
11075
  if (next_token.type != type) return nullptr;
11076
  // Increase parser's index by 1.
11077
  index++;
11078
  // Return next token.
11079
  return &next_token;
11080
}
11081
11082
template <url_pattern_encoding_callback F>
11083
std::string url_pattern_parser<F>::consume_text() {
11084
  // Let result be the empty string.
11085
  std::string result{};
11086
  // While true:
11087
  while (true) {
11088
    // Let token be the result of running try to consume a token given parser
11089
    // and "char".
11090
    auto token = try_consume_token(token_type::CHAR);
11091
    // If token is null, then set token to the result of running try to consume
11092
    // a token given parser and "escaped-char".
11093
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
11094
    // If token is null, then break.
11095
    if (!token) break;
11096
    // Append token's value to the end of result.
11097
    result.append(token->value);
11098
  }
11099
  // Return result.
11100
  return result;
11101
}
11102
11103
template <url_pattern_encoding_callback F>
11104
bool url_pattern_parser<F>::consume_required_token(token_type type) {
11105
  ada_log("url_pattern_parser::consume_required_token called with type=",
11106
          to_string(type));
11107
  // Let result be the result of running try to consume a token given parser and
11108
  // type.
11109
  return try_consume_token(type) != nullptr;
11110
}
11111
11112
template <url_pattern_encoding_callback F>
11113
std::optional<errors>
11114
url_pattern_parser<F>::maybe_add_part_from_the_pending_fixed_value() {
11115
  // If parser's pending fixed value is the empty string, then return.
11116
  if (pending_fixed_value.empty()) {
11117
    ada_log("pending_fixed_value is empty");
11118
    return std::nullopt;
11119
  }
11120
  // Let encoded value be the result of running parser's encoding callback given
11121
  // parser's pending fixed value.
11122
  auto encoded_value = encoding_callback(pending_fixed_value);
11123
  if (!encoded_value) {
11124
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
11125
    return encoded_value.error();
11126
  }
11127
  // Set parser's pending fixed value to the empty string.
11128
  pending_fixed_value.clear();
11129
  // Let part be a new part whose type is "fixed-text", value is encoded value,
11130
  // and modifier is "none".
11131
  // Append part to parser's part list.
11132
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11133
                     std::move(*encoded_value),
11134
                     url_pattern_part_modifier::none);
11135
  return std::nullopt;
11136
}
11137
11138
template <url_pattern_encoding_callback F>
11139
std::optional<errors> url_pattern_parser<F>::add_part(
11140
    std::string_view prefix, token* name_token, token* regexp_or_wildcard_token,
11141
    std::string_view suffix, token* modifier_token) {
11142
  // Let modifier be "none".
11143
  auto modifier = url_pattern_part_modifier::none;
11144
  // If modifier token is not null:
11145
  if (modifier_token) {
11146
    // If modifier token's value is "?" then set modifier to "optional".
11147
    if (modifier_token->value == "?") {
11148
      modifier = url_pattern_part_modifier::optional;
11149
    } else if (modifier_token->value == "*") {
11150
      // Otherwise if modifier token's value is "*" then set modifier to
11151
      // "zero-or-more".
11152
      modifier = url_pattern_part_modifier::zero_or_more;
11153
    } else if (modifier_token->value == "+") {
11154
      // Otherwise if modifier token's value is "+" then set modifier to
11155
      // "one-or-more".
11156
      modifier = url_pattern_part_modifier::one_or_more;
11157
    }
11158
  }
11159
  // If name token is null and regexp or wildcard token is null and modifier
11160
  // is "none":
11161
  if (!name_token && !regexp_or_wildcard_token &&
11162
      modifier == url_pattern_part_modifier::none) {
11163
    // Append prefix to the end of parser's pending fixed value.
11164
    pending_fixed_value.append(prefix);
11165
    return std::nullopt;
11166
  }
11167
  // Run maybe add a part from the pending fixed value given parser.
11168
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
11169
    return *error;
11170
  }
11171
  // If name token is null and regexp or wildcard token is null:
11172
  if (!name_token && !regexp_or_wildcard_token) {
11173
    // Assert: suffix is the empty string.
11174
    ADA_ASSERT_TRUE(suffix.empty());
11175
    // If prefix is the empty string, then return.
11176
    if (prefix.empty()) return std::nullopt;
11177
    // Let encoded value be the result of running parser's encoding callback
11178
    // given prefix.
11179
    auto encoded_value = encoding_callback(prefix);
11180
    if (!encoded_value) {
11181
      return encoded_value.error();
11182
    }
11183
    // Let part be a new part whose type is "fixed-text", value is encoded
11184
    // value, and modifier is modifier.
11185
    // Append part to parser's part list.
11186
    parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11187
                       std::move(*encoded_value), modifier);
11188
    return std::nullopt;
11189
  }
11190
  // Let regexp value be the empty string.
11191
  std::string regexp_value{};
11192
  // If regexp or wildcard token is null, then set regexp value to parser's
11193
  // segment wildcard regexp.
11194
  if (!regexp_or_wildcard_token) {
11195
    regexp_value = segment_wildcard_regexp;
11196
  } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) {
11197
    // Otherwise if regexp or wildcard token's type is "asterisk", then set
11198
    // regexp value to the full wildcard regexp value.
11199
    regexp_value = ".*";
11200
  } else {
11201
    // Otherwise set regexp value to regexp or wildcard token's value.
11202
    regexp_value = regexp_or_wildcard_token->value;
11203
  }
11204
  // Let type be "regexp".
11205
  auto type = url_pattern_part_type::REGEXP;
11206
  // If regexp value is parser's segment wildcard regexp:
11207
  if (regexp_value == segment_wildcard_regexp) {
11208
    // Set type to "segment-wildcard".
11209
    type = url_pattern_part_type::SEGMENT_WILDCARD;
11210
    // Set regexp value to the empty string.
11211
    regexp_value.clear();
11212
  } else if (regexp_value == ".*") {
11213
    // Otherwise if regexp value is the full wildcard regexp value:
11214
    // Set type to "full-wildcard".
11215
    type = url_pattern_part_type::FULL_WILDCARD;
11216
    // Set regexp value to the empty string.
11217
    regexp_value.clear();
11218
  }
11219
  // Let name be the empty string.
11220
  std::string name{};
11221
  // If name token is not null, then set name to name token's value.
11222
  if (name_token) {
11223
    name = name_token->value;
11224
  } else if (regexp_or_wildcard_token != nullptr) {
11225
    // Otherwise if regexp or wildcard token is not null:
11226
    // Set name to parser's next numeric name, serialized.
11227
    name = std::to_string(next_numeric_name);
11228
    // Increment parser's next numeric name by 1.
11229
    next_numeric_name++;
11230
  }
11231
  // If the result of running is a duplicate name given parser and name is
11232
  // true, then throw a TypeError.
11233
  if (std::ranges::any_of(
11234
          parts, [&name](const auto& part) { return part.name == name; })) {
11235
    return errors::type_error;
11236
  }
11237
  // Let encoded prefix be the result of running parser's encoding callback
11238
  // given prefix.
11239
  auto encoded_prefix = encoding_callback(prefix);
11240
  if (!encoded_prefix) return encoded_prefix.error();
11241
  // Let encoded suffix be the result of running parser's encoding callback
11242
  // given suffix.
11243
  auto encoded_suffix = encoding_callback(suffix);
11244
  if (!encoded_suffix) return encoded_suffix.error();
11245
  // Let part be a new part whose type is type, value is regexp value,
11246
  // modifier is modifier, name is name, prefix is encoded prefix, and suffix
11247
  // is encoded suffix.
11248
  // Append part to parser's part list.
11249
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
11250
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
11251
  return std::nullopt;
11252
}
11253
11254
template <url_pattern_encoding_callback F>
11255
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
11256
    std::string_view input, url_pattern_compile_component_options& options,
11257
    F& encoding_callback) {
11258
  ada_log("parse_pattern_string input=", input);
11259
  // Let parser be a new pattern parser whose encoding callback is encoding
11260
  // callback and segment wildcard regexp is the result of running generate a
11261
  // segment wildcard regexp given options.
11262
  auto parser = url_pattern_parser<F>(
11263
      encoding_callback, generate_segment_wildcard_regexp(options));
11264
  // Set parser's token list to the result of running tokenize given input and
11265
  // "strict".
11266
  auto tokenize_result = tokenize(input, token_policy::strict);
11267
  if (!tokenize_result) {
11268
    ada_log("parse_pattern_string tokenize failed");
11269
    return tl::unexpected(tokenize_result.error());
11270
  }
11271
  parser.tokens = std::move(*tokenize_result);
11272
11273
  // While parser's index is less than parser's token list's size:
11274
  while (parser.can_continue()) {
11275
    // Let char token be the result of running try to consume a token given
11276
    // parser and "char".
11277
    auto char_token = parser.try_consume_token(token_type::CHAR);
11278
    // Let name token be the result of running try to consume a token given
11279
    // parser and "name".
11280
    auto name_token = parser.try_consume_token(token_type::NAME);
11281
    // Let regexp or wildcard token be the result of running try to consume a
11282
    // regexp or wildcard token given parser and name token.
11283
    auto regexp_or_wildcard_token =
11284
        parser.try_consume_regexp_or_wildcard_token(name_token);
11285
    // If name token is not null or regexp or wildcard token is not null:
11286
    if (name_token || regexp_or_wildcard_token) {
11287
      // Let prefix be the empty string.
11288
      std::string prefix{};
11289
      // If char token is not null then set prefix to char token's value.
11290
      if (char_token) prefix = char_token->value;
11291
      // If prefix is not the empty string and not options's prefix code point:
11292
      if (!prefix.empty() && prefix != options.get_prefix()) {
11293
        // Append prefix to the end of parser's pending fixed value.
11294
        parser.pending_fixed_value.append(prefix);
11295
        // Set prefix to the empty string.
11296
        prefix.clear();
11297
      }
11298
      // Run maybe add a part from the pending fixed value given parser.
11299
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11300
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
11301
        return tl::unexpected(*error);
11302
      }
11303
      // Let modifier token be the result of running try to consume a modifier
11304
      // token given parser.
11305
      auto modifier_token = parser.try_consume_modifier_token();
11306
      // Run add a part given parser, prefix, name token, regexp or wildcard
11307
      // token, the empty string, and modifier token.
11308
      if (auto error =
11309
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
11310
                              modifier_token)) {
11311
        ada_log("parser.add_part failed");
11312
        return tl::unexpected(*error);
11313
      }
11314
      // Continue.
11315
      continue;
11316
    }
11317
11318
    // Let fixed token be char token.
11319
    auto fixed_token = char_token;
11320
    // If fixed token is null, then set fixed token to the result of running try
11321
    // to consume a token given parser and "escaped-char".
11322
    if (!fixed_token)
11323
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
11324
    // If fixed token is not null:
11325
    if (fixed_token) {
11326
      // Append fixed token's value to parser's pending fixed value.
11327
      parser.pending_fixed_value.append(fixed_token->value);
11328
      // Continue.
11329
      continue;
11330
    }
11331
    // Let open token be the result of running try to consume a token given
11332
    // parser and "open".
11333
    auto open_token = parser.try_consume_token(token_type::OPEN);
11334
    // If open token is not null:
11335
    if (open_token) {
11336
      // Set prefix be the result of running consume text given parser.
11337
      auto prefix_ = parser.consume_text();
11338
      // Set name token to the result of running try to consume a token given
11339
      // parser and "name".
11340
      name_token = parser.try_consume_token(token_type::NAME);
11341
      // Set regexp or wildcard token to the result of running try to consume a
11342
      // regexp or wildcard token given parser and name token.
11343
      regexp_or_wildcard_token =
11344
          parser.try_consume_regexp_or_wildcard_token(name_token);
11345
      // Let suffix be the result of running consume text given parser.
11346
      auto suffix_ = parser.consume_text();
11347
      // Run consume a required token given parser and "close".
11348
      if (!parser.consume_required_token(token_type::CLOSE)) {
11349
        ada_log("parser.consume_required_token failed");
11350
        return tl::unexpected(errors::type_error);
11351
      }
11352
      // Set modifier token to the result of running try to consume a modifier
11353
      // token given parser.
11354
      auto modifier_token = parser.try_consume_modifier_token();
11355
      // Run add a part given parser, prefix, name token, regexp or wildcard
11356
      // token, suffix, and modifier token.
11357
      if (auto error =
11358
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
11359
                              suffix_, modifier_token)) {
11360
        return tl::unexpected(*error);
11361
      }
11362
      // Continue.
11363
      continue;
11364
    }
11365
    // Run maybe add a part from the pending fixed value given parser.
11366
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11367
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
11368
      return tl::unexpected(*error);
11369
    }
11370
    // Run consume a required token given parser and "end".
11371
    if (!parser.consume_required_token(token_type::END)) {
11372
      return tl::unexpected(errors::type_error);
11373
    }
11374
  }
11375
  ada_log("parser.parts size is: ", parser.parts.size());
11376
  // Return parser's part list.
11377
  return parser.parts;
11378
}
11379
11380
template <url_pattern_regex::regex_concept regex_provider>
11381
bool protocol_component_matches_special_scheme(
11382
    url_pattern_component<regex_provider>& component) {
11383
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
11384
  switch (component.type) {
11385
    case url_pattern_component_type::EMPTY:
11386
      // Empty pattern can't match any special scheme
11387
      return false;
11388
    case url_pattern_component_type::EXACT_MATCH:
11389
      // Direct string comparison for exact match patterns
11390
      return component.exact_match_value == "http" ||
11391
             component.exact_match_value == "https" ||
11392
             component.exact_match_value == "ws" ||
11393
             component.exact_match_value == "wss" ||
11394
             component.exact_match_value == "ftp" ||
11395
             component.exact_match_value == "file";
11396
    case url_pattern_component_type::FULL_WILDCARD:
11397
      // Full wildcard matches everything including special schemes
11398
      return true;
11399
    case url_pattern_component_type::REGEXP:
11400
      // Fall back to regex matching for complex patterns
11401
      auto& regex = component.regexp;
11402
      return regex_provider::regex_match("http", regex) ||
11403
             regex_provider::regex_match("https", regex) ||
11404
             regex_provider::regex_match("ws", regex) ||
11405
             regex_provider::regex_match("wss", regex) ||
11406
             regex_provider::regex_match("ftp", regex) ||
11407
             regex_provider::regex_match("file", regex);
11408
  }
11409
  ada::unreachable();
11410
}
11411
11412
template <url_pattern_regex::regex_concept regex_provider>
11413
inline std::optional<errors> constructor_string_parser<
11414
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
11415
  ada_log(
11416
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
11417
      "flag");
11418
  // Let protocol string be the result of running make a component string given
11419
  // parser.
11420
  auto protocol_string = make_component_string();
11421
  // Let protocol component be the result of compiling a component given
11422
  // protocol string, canonicalize a protocol, and default options.
11423
  auto protocol_component = url_pattern_component<regex_provider>::compile(
11424
      protocol_string, canonicalize_protocol,
11425
      url_pattern_compile_component_options::DEFAULT);
11426
  if (!protocol_component) {
11427
    ada_log("url_pattern_component::compile failed for protocol_string ",
11428
            protocol_string);
11429
    return protocol_component.error();
11430
  }
11431
  // If the result of running protocol component matches a special scheme given
11432
  // protocol component is true, then set parser's protocol matches a special
11433
  // scheme flag to true.
11434
  if (protocol_component_matches_special_scheme(*protocol_component)) {
11435
    protocol_matches_a_special_scheme_flag = true;
11436
  }
11437
  return std::nullopt;
11438
}
11439
11440
template <url_pattern_regex::regex_concept regex_provider>
11441
tl::expected<url_pattern_init, errors>
11442
constructor_string_parser<regex_provider>::parse(std::string_view input) {
11443
  ada_log("constructor_string_parser::parse input=", input);
11444
  // Let parser be a new constructor string parser whose input is input and
11445
  // token list is the result of running tokenize given input and "lenient".
11446
  auto token_list = tokenize(input, token_policy::lenient);
11447
  if (!token_list) {
11448
    return tl::unexpected(token_list.error());
11449
  }
11450
  auto parser = constructor_string_parser(input, std::move(*token_list));
11451
11452
  // While parser's token index is less than parser's token list size:
11453
  while (parser.token_index < parser.token_list.size()) {
11454
    // Set parser's token increment to 1.
11455
    parser.token_increment = 1;
11456
11457
    // If parser's token list[parser's token index]'s type is "end" then:
11458
    if (parser.token_list[parser.token_index].type == token_type::END) {
11459
      // If parser's state is "init":
11460
      if (parser.state == State::INIT) {
11461
        // Run rewind given parser.
11462
        parser.rewind();
11463
        // If the result of running is a hash prefix given parser is true, then
11464
        // run change state given parser, "hash" and 1.
11465
        if (parser.is_hash_prefix()) {
11466
          parser.change_state(State::HASH, 1);
11467
        } else if (parser.is_search_prefix()) {
11468
          // Otherwise if the result of running is a search prefix given parser
11469
          // is true: Run change state given parser, "search" and 1.
11470
          parser.change_state(State::SEARCH, 1);
11471
        } else {
11472
          // Run change state given parser, "pathname" and 0.
11473
          parser.change_state(State::PATHNAME, 0);
11474
        }
11475
        // Increment parser's token index by parser's token increment.
11476
        parser.token_index += parser.token_increment;
11477
        // Continue.
11478
        continue;
11479
      }
11480
11481
      if (parser.state == State::AUTHORITY) {
11482
        // If parser's state is "authority":
11483
        // Run rewind and set state given parser, and "hostname".
11484
        parser.rewind();
11485
        parser.change_state(State::HOSTNAME, 0);
11486
        // Increment parser's token index by parser's token increment.
11487
        parser.token_index += parser.token_increment;
11488
        // Continue.
11489
        continue;
11490
      }
11491
11492
      // Run change state given parser, "done" and 0.
11493
      parser.change_state(State::DONE, 0);
11494
      // Break.
11495
      break;
11496
    }
11497
11498
    // If the result of running is a group open given parser is true:
11499
    if (parser.is_group_open()) {
11500
      // Increment parser's group depth by 1.
11501
      parser.group_depth += 1;
11502
      // Increment parser's token index by parser's token increment.
11503
      parser.token_index += parser.token_increment;
11504
      // Continue.
11505
      continue;
11506
    }
11507
11508
    // If parser's group depth is greater than 0:
11509
    if (parser.group_depth > 0) {
11510
      // If the result of running is a group close given parser is true, then
11511
      // decrement parser's group depth by 1.
11512
      if (parser.is_group_close()) {
11513
        parser.group_depth -= 1;
11514
      } else {
11515
        // Increment parser's token index by parser's token increment.
11516
        parser.token_index += parser.token_increment;
11517
        continue;
11518
      }
11519
    }
11520
11521
    // Switch on parser's state and run the associated steps:
11522
    switch (parser.state) {
11523
      case State::INIT: {
11524
        // If the result of running is a protocol suffix given parser is true:
11525
        if (parser.is_protocol_suffix()) {
11526
          // Run rewind and set state given parser and "protocol".
11527
          parser.rewind();
11528
          parser.change_state(State::PROTOCOL, 0);
11529
        }
11530
        break;
11531
      }
11532
      case State::PROTOCOL: {
11533
        // If the result of running is a protocol suffix given parser is true:
11534
        if (parser.is_protocol_suffix()) {
11535
          // Run compute protocol matches a special scheme flag given parser.
11536
          if (const auto error =
11537
                  parser.compute_protocol_matches_special_scheme_flag()) {
11538
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11539
            return tl::unexpected(*error);
11540
          }
11541
          // Let next state be "pathname".
11542
          auto next_state = State::PATHNAME;
11543
          // Let skip be 1.
11544
          auto skip = 1;
11545
          // If the result of running next is authority slashes given parser is
11546
          // true:
11547
          if (parser.next_is_authority_slashes()) {
11548
            // Set next state to "authority".
11549
            next_state = State::AUTHORITY;
11550
            // Set skip to 3.
11551
            skip = 3;
11552
          } else if (parser.protocol_matches_a_special_scheme_flag) {
11553
            // Otherwise if parser's protocol matches a special scheme flag is
11554
            // true, then set next state to "authority".
11555
            next_state = State::AUTHORITY;
11556
          }
11557
11558
          // Run change state given parser, next state, and skip.
11559
          parser.change_state(next_state, skip);
11560
        }
11561
        break;
11562
      }
11563
      case State::AUTHORITY: {
11564
        // If the result of running is an identity terminator given parser is
11565
        // true, then run rewind and set state given parser and "username".
11566
        if (parser.is_an_identity_terminator()) {
11567
          parser.rewind();
11568
          parser.change_state(State::USERNAME, 0);
11569
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11570
                   parser.is_hash_prefix()) {
11571
          // Otherwise if any of the following are true:
11572
          // - the result of running is a pathname start given parser;
11573
          // - the result of running is a search prefix given parser; or
11574
          // - the result of running is a hash prefix given parser,
11575
          // then run rewind and set state given parser and "hostname".
11576
          parser.rewind();
11577
          parser.change_state(State::HOSTNAME, 0);
11578
        }
11579
        break;
11580
      }
11581
      case State::USERNAME: {
11582
        // If the result of running is a password prefix given parser is true,
11583
        // then run change state given parser, "password", and 1.
11584
        if (parser.is_password_prefix()) {
11585
          parser.change_state(State::PASSWORD, 1);
11586
        } else if (parser.is_an_identity_terminator()) {
11587
          // Otherwise if the result of running is an identity terminator given
11588
          // parser is true, then run change state given parser, "hostname",
11589
          // and 1.
11590
          parser.change_state(State::HOSTNAME, 1);
11591
        }
11592
        break;
11593
      }
11594
      case State::PASSWORD: {
11595
        // If the result of running is an identity terminator given parser is
11596
        // true, then run change state given parser, "hostname", and 1.
11597
        if (parser.is_an_identity_terminator()) {
11598
          parser.change_state(State::HOSTNAME, 1);
11599
        }
11600
        break;
11601
      }
11602
      case State::HOSTNAME: {
11603
        // If the result of running is an IPv6 open given parser is true, then
11604
        // increment parser's hostname IPv6 bracket depth by 1.
11605
        if (parser.is_an_ipv6_open()) {
11606
          parser.hostname_ipv6_bracket_depth += 1;
11607
        } else if (parser.is_an_ipv6_close()) {
11608
          // Otherwise if the result of running is an IPv6 close given parser is
11609
          // true, then decrement parser's hostname IPv6 bracket depth by 1.
11610
          parser.hostname_ipv6_bracket_depth -= 1;
11611
        } else if (parser.is_port_prefix() &&
11612
                   parser.hostname_ipv6_bracket_depth == 0) {
11613
          // Otherwise if the result of running is a port prefix given parser is
11614
          // true and parser's hostname IPv6 bracket depth is zero, then run
11615
          // change state given parser, "port", and 1.
11616
          parser.change_state(State::PORT, 1);
11617
        } else if (parser.is_pathname_start()) {
11618
          // Otherwise if the result of running is a pathname start given parser
11619
          // is true, then run change state given parser, "pathname", and 0.
11620
          parser.change_state(State::PATHNAME, 0);
11621
        } else if (parser.is_search_prefix()) {
11622
          // Otherwise if the result of running is a search prefix given parser
11623
          // is true, then run change state given parser, "search", and 1.
11624
          parser.change_state(State::SEARCH, 1);
11625
        } else if (parser.is_hash_prefix()) {
11626
          // Otherwise if the result of running is a hash prefix given parser is
11627
          // true, then run change state given parser, "hash", and 1.
11628
          parser.change_state(State::HASH, 1);
11629
        }
11630
11631
        break;
11632
      }
11633
      case State::PORT: {
11634
        // If the result of running is a pathname start given parser is true,
11635
        // then run change state given parser, "pathname", and 0.
11636
        if (parser.is_pathname_start()) {
11637
          parser.change_state(State::PATHNAME, 0);
11638
        } else if (parser.is_search_prefix()) {
11639
          // Otherwise if the result of running is a search prefix given parser
11640
          // is true, then run change state given parser, "search", and 1.
11641
          parser.change_state(State::SEARCH, 1);
11642
        } else if (parser.is_hash_prefix()) {
11643
          // Otherwise if the result of running is a hash prefix given parser is
11644
          // true, then run change state given parser, "hash", and 1.
11645
          parser.change_state(State::HASH, 1);
11646
        }
11647
        break;
11648
      }
11649
      case State::PATHNAME: {
11650
        // If the result of running is a search prefix given parser is true,
11651
        // then run change state given parser, "search", and 1.
11652
        if (parser.is_search_prefix()) {
11653
          parser.change_state(State::SEARCH, 1);
11654
        } else if (parser.is_hash_prefix()) {
11655
          // Otherwise if the result of running is a hash prefix given parser is
11656
          // true, then run change state given parser, "hash", and 1.
11657
          parser.change_state(State::HASH, 1);
11658
        }
11659
        break;
11660
      }
11661
      case State::SEARCH: {
11662
        // If the result of running is a hash prefix given parser is true, then
11663
        // run change state given parser, "hash", and 1.
11664
        if (parser.is_hash_prefix()) {
11665
          parser.change_state(State::HASH, 1);
11666
        }
11667
        break;
11668
      }
11669
      case State::HASH: {
11670
        // Do nothing
11671
        break;
11672
      }
11673
      default: {
11674
        // Assert: This step is never reached.
11675
        unreachable();
11676
      }
11677
    }
11678
11679
    // Increment parser's token index by parser's token increment.
11680
    parser.token_index += parser.token_increment;
11681
  }
11682
11683
  // If parser's result contains "hostname" and not "port", then set parser's
11684
  // result["port"] to the empty string.
11685
  if (parser.result.hostname && !parser.result.port) {
11686
    parser.result.port = "";
11687
  }
11688
11689
  // Return parser's result.
11690
  return parser.result;
11691
}
11692
11693
}  // namespace ada::url_pattern_helpers
11694
#endif  // ADA_INCLUDE_URL_PATTERN
11695
#endif
11696
/* end file include/ada/url_pattern_helpers-inl.h */
11697
11698
// Public API
11699
/* begin file include/ada/ada_version.h */
11700
/**
11701
 * @file ada_version.h
11702
 * @brief Definitions for Ada's version number.
11703
 */
11704
#ifndef ADA_ADA_VERSION_H
11705
#define ADA_ADA_VERSION_H
11706
11707
13.5k
#define ADA_VERSION "4.0.0"
11708
11709
namespace ada {
11710
11711
enum {
11712
  ADA_VERSION_MAJOR = 4,
11713
  ADA_VERSION_MINOR = 0,
11714
  ADA_VERSION_REVISION = 0,
11715
};
11716
11717
}  // namespace ada
11718
11719
#endif  // ADA_ADA_VERSION_H
11720
/* end file include/ada/ada_version.h */
11721
/* begin file include/ada/implementation-inl.h */
11722
/**
11723
 * @file implementation-inl.h
11724
 */
11725
#ifndef ADA_IMPLEMENTATION_INL_H
11726
#define ADA_IMPLEMENTATION_INL_H
11727
11728
11729
11730
#include <variant>
11731
#include <string_view>
11732
11733
namespace ada {
11734
11735
#if ADA_INCLUDE_URL_PATTERN
11736
template <url_pattern_regex::regex_concept regex_provider>
11737
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
11738
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
11739
                  const std::string_view* base_url,
11740
                  const url_pattern_options* options) {
11741
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
11742
                                                        base_url, options);
11743
}
11744
#endif  // ADA_INCLUDE_URL_PATTERN
11745
11746
}  // namespace ada
11747
11748
#endif  // ADA_IMPLEMENTATION_INL_H
11749
/* end file include/ada/implementation-inl.h */
11750
11751
#endif  // ADA_H
11752
/* end file include/ada.h */