Coverage Report

Created: 2026-03-11 06:43

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-03-08 22:49:10 -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-01-30 12:00:02 -0500. 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
73
}  // namespace ada::idna
74
75
#endif
76
/* end file include/ada/idna/mapping.h */
77
/* begin file include/ada/idna/normalization.h */
78
#ifndef ADA_IDNA_NORMALIZATION_H
79
#define ADA_IDNA_NORMALIZATION_H
80
81
#include <string>
82
#include <string_view>
83
84
namespace ada::idna {
85
86
// Normalize the characters according to IDNA (Unicode Normalization Form C).
87
void normalize(std::u32string& input);
88
89
}  // namespace ada::idna
90
#endif
91
/* end file include/ada/idna/normalization.h */
92
/* begin file include/ada/idna/punycode.h */
93
#ifndef ADA_IDNA_PUNYCODE_H
94
#define ADA_IDNA_PUNYCODE_H
95
96
#include <string>
97
#include <string_view>
98
99
namespace ada::idna {
100
101
bool punycode_to_utf32(std::string_view input, std::u32string& out);
102
bool verify_punycode(std::string_view input);
103
bool utf32_to_punycode(std::u32string_view input, std::string& out);
104
105
}  // namespace ada::idna
106
107
#endif  // ADA_IDNA_PUNYCODE_H
108
/* end file include/ada/idna/punycode.h */
109
/* begin file include/ada/idna/validity.h */
110
#ifndef ADA_IDNA_VALIDITY_H
111
#define ADA_IDNA_VALIDITY_H
112
113
#include <string>
114
#include <string_view>
115
116
namespace ada::idna {
117
118
/**
119
 * @see https://www.unicode.org/reports/tr46/#Validity_Criteria
120
 */
121
bool is_label_valid(std::u32string_view label);
122
123
}  // namespace ada::idna
124
125
#endif  // ADA_IDNA_VALIDITY_H
126
/* end file include/ada/idna/validity.h */
127
/* begin file include/ada/idna/to_ascii.h */
128
#ifndef ADA_IDNA_TO_ASCII_H
129
#define ADA_IDNA_TO_ASCII_H
130
131
#include <string>
132
#include <string_view>
133
134
namespace ada::idna {
135
136
// Converts a domain (e.g., www.google.com) possibly containing international
137
// characters to an ascii domain (with punycode). It will not do percent
138
// decoding: percent decoding should be done prior to calling this function. We
139
// do not remove tabs and spaces, they should have been removed prior to calling
140
// this function. We also do not trim control characters. We also assume that
141
// the input is not empty. We return "" on error.
142
//
143
//
144
// This function may accept or even produce invalid domains.
145
std::string to_ascii(std::string_view ut8_string);
146
147
// Returns true if the string contains a forbidden code point according to the
148
// WHATGL URL specification:
149
// https://url.spec.whatwg.org/#forbidden-domain-code-point
150
bool contains_forbidden_domain_code_point(std::string_view ascii_string);
151
152
bool constexpr is_ascii(std::u32string_view view);
153
bool constexpr is_ascii(std::string_view view);
154
155
}  // namespace ada::idna
156
157
#endif  // ADA_IDNA_TO_ASCII_H
158
/* end file include/ada/idna/to_ascii.h */
159
/* begin file include/ada/idna/to_unicode.h */
160
161
#ifndef ADA_IDNA_TO_UNICODE_H
162
#define ADA_IDNA_TO_UNICODE_H
163
164
#include <string_view>
165
166
namespace ada::idna {
167
168
std::string to_unicode(std::string_view input);
169
170
}  // namespace ada::idna
171
172
#endif  // ADA_IDNA_TO_UNICODE_H
173
/* end file include/ada/idna/to_unicode.h */
174
/* begin file include/ada/idna/identifier.h */
175
#ifndef ADA_IDNA_IDENTIFIER_H
176
#define ADA_IDNA_IDENTIFIER_H
177
178
#include <string>
179
#include <string_view>
180
181
namespace ada::idna {
182
183
// Verify if it is valid name code point given a Unicode code point and a
184
// boolean first: If first is true return the result of checking if code point
185
// is contained in the IdentifierStart set of code points. Otherwise return the
186
// result of checking if code point is contained in the IdentifierPart set of
187
// code points. Returns false if the input is empty or the code point is not
188
// valid. There is minimal Unicode error handling: the input should be valid
189
// UTF-8. https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point
190
bool valid_name_code_point(char32_t code_point, bool first);
191
192
}  // namespace ada::idna
193
194
#endif
195
/* end file include/ada/idna/identifier.h */
196
197
#endif
198
/* end file include/idna.h */
199
/* end file include/ada/ada_idna.h */
200
/* begin file include/ada/character_sets.h */
201
/**
202
 * @file character_sets.h
203
 * @brief Declaration of the character sets used by unicode functions.
204
 * @author Node.js
205
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
206
 */
207
#ifndef ADA_CHARACTER_SETS_H
208
#define ADA_CHARACTER_SETS_H
209
210
/* begin file include/ada/common_defs.h */
211
/**
212
 * @file common_defs.h
213
 * @brief Cross-platform compiler macros and common definitions.
214
 *
215
 * This header provides compiler-specific macros for optimization hints,
216
 * platform detection, SIMD support detection, and development/debug utilities.
217
 * It ensures consistent behavior across different compilers (GCC, Clang, MSVC).
218
 */
219
#ifndef ADA_COMMON_DEFS_H
220
#define ADA_COMMON_DEFS_H
221
222
// https://en.cppreference.com/w/cpp/feature_test#Library_features
223
// detect C++20 features
224
#include <version>
225
226
#ifdef _MSC_VER
227
#define ADA_VISUAL_STUDIO 1
228
/**
229
 * We want to differentiate carefully between
230
 * clang under visual studio and regular visual
231
 * studio.
232
 */
233
#ifdef __clang__
234
// clang under visual studio
235
#define ADA_CLANG_VISUAL_STUDIO 1
236
#else
237
// just regular visual studio (best guess)
238
#define ADA_REGULAR_VISUAL_STUDIO 1
239
#endif  // __clang__
240
#endif  // _MSC_VER
241
242
#if defined(__GNUC__)
243
// Marks a block with a name so that MCA analysis can see it.
244
#define ADA_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
245
#define ADA_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
246
#define ADA_DEBUG_BLOCK(name, block) \
247
  BEGIN_DEBUG_BLOCK(name);           \
248
  block;                             \
249
  END_DEBUG_BLOCK(name);
250
#else
251
#define ADA_BEGIN_DEBUG_BLOCK(name)
252
#define ADA_END_DEBUG_BLOCK(name)
253
#define ADA_DEBUG_BLOCK(name, block)
254
#endif
255
256
// Align to N-byte boundary
257
#define ADA_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1))
258
#define ADA_ROUNDDOWN_N(a, n) ((a) & ~((n)-1))
259
260
#define ADA_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0)
261
262
#if defined(ADA_REGULAR_VISUAL_STUDIO)
263
264
#define ada_really_inline __forceinline
265
#define ada_never_inline __declspec(noinline)
266
267
#define ada_unused
268
#define ada_warn_unused
269
270
#define ADA_PUSH_DISABLE_WARNINGS __pragma(warning(push))
271
#define ADA_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0))
272
#define ADA_DISABLE_VS_WARNING(WARNING_NUMBER) \
273
  __pragma(warning(disable : WARNING_NUMBER))
274
// Get rid of Intellisense-only warnings (Code Analysis)
275
// Though __has_include is C++17, it is supported in Visual Studio 2017 or
276
// better (_MSC_VER>=1910).
277
#ifdef __has_include
278
#if __has_include(<CppCoreCheck\Warnings.h>)
279
#include <CppCoreCheck\Warnings.h>
280
#define ADA_DISABLE_UNDESIRED_WARNINGS \
281
  ADA_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
282
#endif
283
#endif
284
285
#ifndef ADA_DISABLE_UNDESIRED_WARNINGS
286
#define ADA_DISABLE_UNDESIRED_WARNINGS
287
#endif
288
289
#define ADA_DISABLE_DEPRECATED_WARNING ADA_DISABLE_VS_WARNING(4996)
290
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING
291
#define ADA_POP_DISABLE_WARNINGS __pragma(warning(pop))
292
293
#else  // ADA_REGULAR_VISUAL_STUDIO
294
295
#define ada_really_inline inline __attribute__((always_inline))
296
#define ada_never_inline inline __attribute__((noinline))
297
298
#define ada_unused __attribute__((unused))
299
#define ada_warn_unused __attribute__((warn_unused_result))
300
301
#define ADA_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
302
// gcc doesn't seem to disable all warnings with all and extra, add warnings
303
// here as necessary
304
#define ADA_PUSH_DISABLE_ALL_WARNINGS               \
305
  ADA_PUSH_DISABLE_WARNINGS                         \
306
  ADA_DISABLE_GCC_WARNING("-Weffc++")               \
307
  ADA_DISABLE_GCC_WARNING("-Wall")                  \
308
  ADA_DISABLE_GCC_WARNING("-Wconversion")           \
309
  ADA_DISABLE_GCC_WARNING("-Wextra")                \
310
  ADA_DISABLE_GCC_WARNING("-Wattributes")           \
311
  ADA_DISABLE_GCC_WARNING("-Wimplicit-fallthrough") \
312
  ADA_DISABLE_GCC_WARNING("-Wnon-virtual-dtor")     \
313
  ADA_DISABLE_GCC_WARNING("-Wreturn-type")          \
314
  ADA_DISABLE_GCC_WARNING("-Wshadow")               \
315
  ADA_DISABLE_GCC_WARNING("-Wunused-parameter")     \
316
  ADA_DISABLE_GCC_WARNING("-Wunused-variable")      \
317
  ADA_DISABLE_GCC_WARNING("-Wsign-compare")
318
#define ADA_PRAGMA(P) _Pragma(#P)
319
#define ADA_DISABLE_GCC_WARNING(WARNING) \
320
  ADA_PRAGMA(GCC diagnostic ignored WARNING)
321
#if defined(ADA_CLANG_VISUAL_STUDIO)
322
#define ADA_DISABLE_UNDESIRED_WARNINGS \
323
  ADA_DISABLE_GCC_WARNING("-Wmicrosoft-include")
324
#else
325
#define ADA_DISABLE_UNDESIRED_WARNINGS
326
#endif
327
#define ADA_DISABLE_DEPRECATED_WARNING \
328
  ADA_DISABLE_GCC_WARNING("-Wdeprecated-declarations")
329
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING \
330
  ADA_DISABLE_GCC_WARNING("-Wstrict-overflow")
331
#define ADA_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
332
333
#endif  // MSC_VER
334
335
#if defined(ADA_VISUAL_STUDIO)
336
/**
337
 * It does not matter here whether you are using
338
 * the regular visual studio or clang under visual
339
 * studio.
340
 */
341
#if ADA_USING_LIBRARY
342
#define ADA_DLLIMPORTEXPORT __declspec(dllimport)
343
#else
344
#define ADA_DLLIMPORTEXPORT __declspec(dllexport)
345
#endif
346
#else
347
#define ADA_DLLIMPORTEXPORT
348
#endif
349
350
/// If EXPR is an error, returns it.
351
#define ADA_TRY(EXPR)   \
352
  {                     \
353
    auto _err = (EXPR); \
354
    if (_err) {         \
355
      return _err;      \
356
    }                   \
357
  }
358
359
// __has_cpp_attribute is part of C++20
360
#if !defined(__has_cpp_attribute)
361
#define __has_cpp_attribute(x) 0
362
#endif
363
364
#if __has_cpp_attribute(gnu::noinline)
365
#define ADA_ATTRIBUTE_NOINLINE [[gnu::noinline]]
366
#else
367
#define ADA_ATTRIBUTE_NOINLINE
368
#endif
369
370
namespace ada {
371
0
[[noreturn]] inline void unreachable() {
372
0
#ifdef __GNUC__
373
0
  __builtin_unreachable();
374
#elif defined(_MSC_VER)
375
  __assume(false);
376
#else
377
#endif
378
0
}
379
}  // namespace ada
380
381
// Unless the programmer has already set ADA_DEVELOPMENT_CHECKS,
382
// we want to set it under debug builds. We detect a debug build
383
// under Visual Studio when the _DEBUG macro is set. Under the other
384
// compilers, we use the fact that they define __OPTIMIZE__ whenever
385
// they allow optimizations.
386
// It is possible that this could miss some cases where ADA_DEVELOPMENT_CHECKS
387
// is helpful, but the programmer can set the macro ADA_DEVELOPMENT_CHECKS.
388
// It could also wrongly set ADA_DEVELOPMENT_CHECKS (e.g., if the programmer
389
// sets _DEBUG in a release build under Visual Studio, or if some compiler fails
390
// to set the __OPTIMIZE__ macro).
391
#if !defined(ADA_DEVELOPMENT_CHECKS) && !defined(NDEBUG)
392
#ifdef _MSC_VER
393
// Visual Studio seems to set _DEBUG for debug builds.
394
#ifdef _DEBUG
395
#define ADA_DEVELOPMENT_CHECKS 1
396
#endif  // _DEBUG
397
#else   // _MSC_VER
398
// All other compilers appear to set __OPTIMIZE__ to a positive integer
399
// when the compiler is optimizing.
400
#ifndef __OPTIMIZE__
401
#define ADA_DEVELOPMENT_CHECKS 1
402
#endif  // __OPTIMIZE__
403
#endif  // _MSC_VER
404
#endif  // ADA_DEVELOPMENT_CHECKS
405
406
#define ADA_STR(x) #x
407
408
#if ADA_DEVELOPMENT_CHECKS
409
#define ADA_REQUIRE(EXPR) \
410
  {                       \
411
    if (!(EXPR) { abort(); }) }
412
413
#define ADA_FAIL(MESSAGE)                            \
414
  do {                                               \
415
    std::cerr << "FAIL: " << (MESSAGE) << std::endl; \
416
    abort();                                         \
417
  } while (0);
418
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)                                    \
419
  do {                                                                         \
420
    if (LHS != RHS) {                                                          \
421
      std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \
422
      ADA_FAIL(MESSAGE);                                                       \
423
    }                                                                          \
424
  } while (0);
425
#define ADA_ASSERT_TRUE(COND)                                               \
426
  do {                                                                      \
427
    if (!(COND)) {                                                          \
428
      std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \
429
                << std::endl;                                               \
430
      ADA_FAIL(ADA_STR(COND));                                              \
431
    }                                                                       \
432
  } while (0);
433
#else
434
#define ADA_FAIL(MESSAGE)
435
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)
436
#define ADA_ASSERT_TRUE(COND)
437
#endif
438
439
#ifdef ADA_VISUAL_STUDIO
440
#define ADA_ASSUME(COND) __assume(COND)
441
#else
442
#define ADA_ASSUME(COND)       \
443
  do {                         \
444
    if (!(COND)) {             \
445
      __builtin_unreachable(); \
446
    }                          \
447
  } while (0)
448
#endif
449
450
#if defined(__SSSE3__)
451
#define ADA_SSSE3 1
452
#endif
453
454
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
455
    (defined(_M_AMD64) || defined(_M_X64) ||                         \
456
     (defined(_M_IX86_FP) && _M_IX86_FP == 2))
457
#define ADA_SSE2 1
458
#endif
459
460
#if defined(__aarch64__) || defined(_M_ARM64)
461
#define ADA_NEON 1
462
#endif
463
464
#if defined(__loongarch_sx)
465
#define ADA_LSX 1
466
#endif
467
468
#if defined(__riscv_v) && __riscv_v_intrinsic >= 11000
469
// Support RVV intrinsics v0.11 and above
470
#define ADA_RVV 1
471
#endif
472
473
#ifndef __has_cpp_attribute
474
#define ada_lifetime_bound
475
#elif __has_cpp_attribute(msvc::lifetimebound)
476
#define ada_lifetime_bound [[msvc::lifetimebound]]
477
#elif __has_cpp_attribute(clang::lifetimebound)
478
#define ada_lifetime_bound [[clang::lifetimebound]]
479
#elif __has_cpp_attribute(lifetimebound)
480
#define ada_lifetime_bound [[lifetimebound]]
481
#else
482
#define ada_lifetime_bound
483
#endif
484
485
#ifdef __cpp_lib_format
486
#if __cpp_lib_format >= 202110L
487
#include <format>
488
#define ADA_HAS_FORMAT 1
489
#endif
490
#endif
491
492
#ifndef ADA_INCLUDE_URL_PATTERN
493
#define ADA_INCLUDE_URL_PATTERN 1
494
#endif  // ADA_INCLUDE_URL_PATTERN
495
496
#endif  // ADA_COMMON_DEFS_H
497
/* end file include/ada/common_defs.h */
498
#include <cstdint>
499
500
/**
501
 * These functions are not part of our public API and may
502
 * change at any time.
503
 * @private
504
 * @namespace ada::character_sets
505
 * @brief Includes the definitions for unicode character sets.
506
 */
507
namespace ada::character_sets {
508
ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i);
509
}  // namespace ada::character_sets
510
511
#endif  // ADA_CHARACTER_SETS_H
512
/* end file include/ada/character_sets.h */
513
/* begin file include/ada/character_sets-inl.h */
514
/**
515
 * @file character_sets-inl.h
516
 * @brief Definitions of the character sets used by unicode functions.
517
 * @author Node.js
518
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
519
 */
520
#ifndef ADA_CHARACTER_SETS_INL_H
521
#define ADA_CHARACTER_SETS_INL_H
522
523
524
/**
525
 * These functions are not part of our public API and may
526
 * change at any time.
527
 * @private
528
 */
529
namespace ada::character_sets {
530
531
constexpr char hex[1024] =
532
    "%00\0%01\0%02\0%03\0%04\0%05\0%06\0%07\0"
533
    "%08\0%09\0%0A\0%0B\0%0C\0%0D\0%0E\0%0F\0"
534
    "%10\0%11\0%12\0%13\0%14\0%15\0%16\0%17\0"
535
    "%18\0%19\0%1A\0%1B\0%1C\0%1D\0%1E\0%1F\0"
536
    "%20\0%21\0%22\0%23\0%24\0%25\0%26\0%27\0"
537
    "%28\0%29\0%2A\0%2B\0%2C\0%2D\0%2E\0%2F\0"
538
    "%30\0%31\0%32\0%33\0%34\0%35\0%36\0%37\0"
539
    "%38\0%39\0%3A\0%3B\0%3C\0%3D\0%3E\0%3F\0"
540
    "%40\0%41\0%42\0%43\0%44\0%45\0%46\0%47\0"
541
    "%48\0%49\0%4A\0%4B\0%4C\0%4D\0%4E\0%4F\0"
542
    "%50\0%51\0%52\0%53\0%54\0%55\0%56\0%57\0"
543
    "%58\0%59\0%5A\0%5B\0%5C\0%5D\0%5E\0%5F\0"
544
    "%60\0%61\0%62\0%63\0%64\0%65\0%66\0%67\0"
545
    "%68\0%69\0%6A\0%6B\0%6C\0%6D\0%6E\0%6F\0"
546
    "%70\0%71\0%72\0%73\0%74\0%75\0%76\0%77\0"
547
    "%78\0%79\0%7A\0%7B\0%7C\0%7D\0%7E\0%7F\0"
548
    "%80\0%81\0%82\0%83\0%84\0%85\0%86\0%87\0"
549
    "%88\0%89\0%8A\0%8B\0%8C\0%8D\0%8E\0%8F\0"
550
    "%90\0%91\0%92\0%93\0%94\0%95\0%96\0%97\0"
551
    "%98\0%99\0%9A\0%9B\0%9C\0%9D\0%9E\0%9F\0"
552
    "%A0\0%A1\0%A2\0%A3\0%A4\0%A5\0%A6\0%A7\0"
553
    "%A8\0%A9\0%AA\0%AB\0%AC\0%AD\0%AE\0%AF\0"
554
    "%B0\0%B1\0%B2\0%B3\0%B4\0%B5\0%B6\0%B7\0"
555
    "%B8\0%B9\0%BA\0%BB\0%BC\0%BD\0%BE\0%BF\0"
556
    "%C0\0%C1\0%C2\0%C3\0%C4\0%C5\0%C6\0%C7\0"
557
    "%C8\0%C9\0%CA\0%CB\0%CC\0%CD\0%CE\0%CF\0"
558
    "%D0\0%D1\0%D2\0%D3\0%D4\0%D5\0%D6\0%D7\0"
559
    "%D8\0%D9\0%DA\0%DB\0%DC\0%DD\0%DE\0%DF\0"
560
    "%E0\0%E1\0%E2\0%E3\0%E4\0%E5\0%E6\0%E7\0"
561
    "%E8\0%E9\0%EA\0%EB\0%EC\0%ED\0%EE\0%EF\0"
562
    "%F0\0%F1\0%F2\0%F3\0%F4\0%F5\0%F6\0%F7\0"
563
    "%F8\0%F9\0%FA\0%FB\0%FC\0%FD\0%FE\0%FF";
564
565
constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32] = {
566
    // 00     01     02     03     04     05     06     07
567
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
568
    // 08     09     0A     0B     0C     0D     0E     0F
569
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
570
    // 10     11     12     13     14     15     16     17
571
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
572
    // 18     19     1A     1B     1C     1D     1E     1F
573
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
574
    // 20     21     22     23     24     25     26     27
575
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
576
    // 28     29     2A     2B     2C     2D     2E     2F
577
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
578
    // 30     31     32     33     34     35     36     37
579
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
580
    // 38     39     3A     3B     3C     3D     3E     3F
581
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
582
    // 40     41     42     43     44     45     46     47
583
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
584
    // 48     49     4A     4B     4C     4D     4E     4F
585
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
586
    // 50     51     52     53     54     55     56     57
587
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
588
    // 58     59     5A     5B     5C     5D     5E     5F
589
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
590
    // 60     61     62     63     64     65     66     67
591
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
592
    // 68     69     6A     6B     6C     6D     6E     6F
593
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
594
    // 70     71     72     73     74     75     76     77
595
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
596
    // 78     79     7A     7B     7C     7D     7E     7F
597
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
598
    // 80     81     82     83     84     85     86     87
599
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
600
    // 88     89     8A     8B     8C     8D     8E     8F
601
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
602
    // 90     91     92     93     94     95     96     97
603
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
604
    // 98     99     9A     9B     9C     9D     9E     9F
605
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
606
    // A0     A1     A2     A3     A4     A5     A6     A7
607
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
608
    // A8     A9     AA     AB     AC     AD     AE     AF
609
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
610
    // B0     B1     B2     B3     B4     B5     B6     B7
611
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
612
    // B8     B9     BA     BB     BC     BD     BE     BF
613
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
614
    // C0     C1     C2     C3     C4     C5     C6     C7
615
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
616
    // C8     C9     CA     CB     CC     CD     CE     CF
617
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
618
    // D0     D1     D2     D3     D4     D5     D6     D7
619
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
620
    // D8     D9     DA     DB     DC     DD     DE     DF
621
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
622
    // E0     E1     E2     E3     E4     E5     E6     E7
623
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
624
    // E8     E9     EA     EB     EC     ED     EE     EF
625
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
626
    // F0     F1     F2     F3     F4     F5     F6     F7
627
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
628
    // F8     F9     FA     FB     FC     FD     FE     FF
629
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
630
631
constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32] = {
632
    // 00     01     02     03     04     05     06     07
633
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
634
    // 08     09     0A     0B     0C     0D     0E     0F
635
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
636
    // 10     11     12     13     14     15     16     17
637
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
638
    // 18     19     1A     1B     1C     1D     1E     1F
639
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
640
    // 20     21     22     23     24     25     26     27
641
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x80,
642
    // 28     29     2A     2B     2C     2D     2E     2F
643
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
644
    // 30     31     32     33     34     35     36     37
645
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
646
    // 38     39     3A     3B     3C     3D     3E     3F
647
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
648
    // 40     41     42     43     44     45     46     47
649
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
650
    // 48     49     4A     4B     4C     4D     4E     4F
651
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
652
    // 50     51     52     53     54     55     56     57
653
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
654
    // 58     59     5A     5B     5C     5D     5E     5F
655
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
656
    // 60     61     62     63     64     65     66     67
657
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
658
    // 68     69     6A     6B     6C     6D     6E     6F
659
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
660
    // 70     71     72     73     74     75     76     77
661
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
662
    // 78     79     7A     7B     7C     7D     7E     7F
663
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
664
    // 80     81     82     83     84     85     86     87
665
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
666
    // 88     89     8A     8B     8C     8D     8E     8F
667
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
668
    // 90     91     92     93     94     95     96     97
669
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
670
    // 98     99     9A     9B     9C     9D     9E     9F
671
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
672
    // A0     A1     A2     A3     A4     A5     A6     A7
673
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
674
    // A8     A9     AA     AB     AC     AD     AE     AF
675
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
676
    // B0     B1     B2     B3     B4     B5     B6     B7
677
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
678
    // B8     B9     BA     BB     BC     BD     BE     BF
679
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
680
    // C0     C1     C2     C3     C4     C5     C6     C7
681
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
682
    // C8     C9     CA     CB     CC     CD     CE     CF
683
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
684
    // D0     D1     D2     D3     D4     D5     D6     D7
685
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
686
    // D8     D9     DA     DB     DC     DD     DE     DF
687
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
688
    // E0     E1     E2     E3     E4     E5     E6     E7
689
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
690
    // E8     E9     EA     EB     EC     ED     EE     EF
691
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
692
    // F0     F1     F2     F3     F4     F5     F6     F7
693
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
694
    // F8     F9     FA     FB     FC     FD     FE     FF
695
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
696
697
constexpr uint8_t QUERY_PERCENT_ENCODE[32] = {
698
    // 00     01     02     03     04     05     06     07
699
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
700
    // 08     09     0A     0B     0C     0D     0E     0F
701
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
702
    // 10     11     12     13     14     15     16     17
703
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
704
    // 18     19     1A     1B     1C     1D     1E     1F
705
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
706
    // 20     21     22     23     24     25     26     27
707
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
708
    // 28     29     2A     2B     2C     2D     2E     2F
709
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
710
    // 30     31     32     33     34     35     36     37
711
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
712
    // 38     39     3A     3B     3C     3D     3E     3F
713
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
714
    // 40     41     42     43     44     45     46     47
715
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
716
    // 48     49     4A     4B     4C     4D     4E     4F
717
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
718
    // 50     51     52     53     54     55     56     57
719
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
720
    // 58     59     5A     5B     5C     5D     5E     5F
721
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
722
    // 60     61     62     63     64     65     66     67
723
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
724
    // 68     69     6A     6B     6C     6D     6E     6F
725
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
726
    // 70     71     72     73     74     75     76     77
727
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
728
    // 78     79     7A     7B     7C     7D     7E     7F
729
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
730
    // 80     81     82     83     84     85     86     87
731
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
732
    // 88     89     8A     8B     8C     8D     8E     8F
733
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
734
    // 90     91     92     93     94     95     96     97
735
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
736
    // 98     99     9A     9B     9C     9D     9E     9F
737
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
738
    // A0     A1     A2     A3     A4     A5     A6     A7
739
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
740
    // A8     A9     AA     AB     AC     AD     AE     AF
741
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
742
    // B0     B1     B2     B3     B4     B5     B6     B7
743
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
744
    // B8     B9     BA     BB     BC     BD     BE     BF
745
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
746
    // C0     C1     C2     C3     C4     C5     C6     C7
747
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
748
    // C8     C9     CA     CB     CC     CD     CE     CF
749
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
750
    // D0     D1     D2     D3     D4     D5     D6     D7
751
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
752
    // D8     D9     DA     DB     DC     DD     DE     DF
753
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
754
    // E0     E1     E2     E3     E4     E5     E6     E7
755
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
756
    // E8     E9     EA     EB     EC     ED     EE     EF
757
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
758
    // F0     F1     F2     F3     F4     F5     F6     F7
759
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
760
    // F8     F9     FA     FB     FC     FD     FE     FF
761
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
762
763
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32] = {
764
    // 00     01     02     03     04     05     06     07
765
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
766
    // 08     09     0A     0B     0C     0D     0E     0F
767
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
768
    // 10     11     12     13     14     15     16     17
769
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
770
    // 18     19     1A     1B     1C     1D     1E     1F
771
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
772
    // 20     21     22     23     24     25     26     27
773
    0x01 | 0x00 | 0x04 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
774
    // 28     29     2A     2B     2C     2D     2E     2F
775
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
776
    // 30     31     32     33     34     35     36     37
777
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
778
    // 38     39     3A     3B     3C     3D     3E     3F
779
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
780
    // 40     41     42     43     44     45     46     47
781
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
782
    // 48     49     4A     4B     4C     4D     4E     4F
783
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
784
    // 50     51     52     53     54     55     56     57
785
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
786
    // 58     59     5A     5B     5C     5D     5E     5F
787
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
788
    // 60     61     62     63     64     65     66     67
789
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
790
    // 68     69     6A     6B     6C     6D     6E     6F
791
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
792
    // 70     71     72     73     74     75     76     77
793
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
794
    // 78     79     7A     7B     7C     7D     7E     7F
795
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
796
    // 80     81     82     83     84     85     86     87
797
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
798
    // 88     89     8A     8B     8C     8D     8E     8F
799
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
800
    // 90     91     92     93     94     95     96     97
801
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
802
    // 98     99     9A     9B     9C     9D     9E     9F
803
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
804
    // A0     A1     A2     A3     A4     A5     A6     A7
805
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
806
    // A8     A9     AA     AB     AC     AD     AE     AF
807
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
808
    // B0     B1     B2     B3     B4     B5     B6     B7
809
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
810
    // B8     B9     BA     BB     BC     BD     BE     BF
811
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
812
    // C0     C1     C2     C3     C4     C5     C6     C7
813
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
814
    // C8     C9     CA     CB     CC     CD     CE     CF
815
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
816
    // D0     D1     D2     D3     D4     D5     D6     D7
817
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
818
    // D8     D9     DA     DB     DC     DD     DE     DF
819
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
820
    // E0     E1     E2     E3     E4     E5     E6     E7
821
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
822
    // E8     E9     EA     EB     EC     ED     EE     EF
823
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
824
    // F0     F1     F2     F3     F4     F5     F6     F7
825
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
826
    // F8     F9     FA     FB     FC     FD     FE     FF
827
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
828
829
constexpr uint8_t USERINFO_PERCENT_ENCODE[32] = {
830
    // 00     01     02     03     04     05     06     07
831
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
832
    // 08     09     0A     0B     0C     0D     0E     0F
833
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
834
    // 10     11     12     13     14     15     16     17
835
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
836
    // 18     19     1A     1B     1C     1D     1E     1F
837
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
838
    // 20     21     22     23     24     25     26     27
839
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
840
    // 28     29     2A     2B     2C     2D     2E     2F
841
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
842
    // 30     31     32     33     34     35     36     37
843
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
844
    // 38     39     3A     3B     3C     3D     3E     3F
845
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
846
    // 40     41     42     43     44     45     46     47
847
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
848
    // 48     49     4A     4B     4C     4D     4E     4F
849
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
850
    // 50     51     52     53     54     55     56     57
851
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
852
    // 58     59     5A     5B     5C     5D     5E     5F
853
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
854
    // 60     61     62     63     64     65     66     67
855
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
856
    // 68     69     6A     6B     6C     6D     6E     6F
857
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
858
    // 70     71     72     73     74     75     76     77
859
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
860
    // 78     79     7A     7B     7C     7D     7E     7F
861
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80,
862
    // 80     81     82     83     84     85     86     87
863
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
864
    // 88     89     8A     8B     8C     8D     8E     8F
865
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
866
    // 90     91     92     93     94     95     96     97
867
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
868
    // 98     99     9A     9B     9C     9D     9E     9F
869
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
870
    // A0     A1     A2     A3     A4     A5     A6     A7
871
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
872
    // A8     A9     AA     AB     AC     AD     AE     AF
873
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
874
    // B0     B1     B2     B3     B4     B5     B6     B7
875
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
876
    // B8     B9     BA     BB     BC     BD     BE     BF
877
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
878
    // C0     C1     C2     C3     C4     C5     C6     C7
879
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
880
    // C8     C9     CA     CB     CC     CD     CE     CF
881
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
882
    // D0     D1     D2     D3     D4     D5     D6     D7
883
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
884
    // D8     D9     DA     DB     DC     DD     DE     DF
885
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
886
    // E0     E1     E2     E3     E4     E5     E6     E7
887
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
888
    // E8     E9     EA     EB     EC     ED     EE     EF
889
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
890
    // F0     F1     F2     F3     F4     F5     F6     F7
891
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
892
    // F8     F9     FA     FB     FC     FD     FE     FF
893
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
894
895
constexpr uint8_t PATH_PERCENT_ENCODE[32] = {
896
    // 00     01     02     03     04     05     06     07
897
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
898
    // 08     09     0A     0B     0C     0D     0E     0F
899
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
900
    // 10     11     12     13     14     15     16     17
901
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
902
    // 18     19     1A     1B     1C     1D     1E     1F
903
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
904
    // 20     21     22     23     24     25     26     27
905
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
906
    // 28     29     2A     2B     2C     2D     2E     2F
907
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
908
    // 30     31     32     33     34     35     36     37
909
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
910
    // 38     39     3A     3B     3C     3D     3E     3F
911
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
912
    // 40     41     42     43     44     45     46     47
913
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
914
    // 48     49     4A     4B     4C     4D     4E     4F
915
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
916
    // 50     51     52     53     54     55     56     57
917
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
918
    // 58     59     5A     5B     5C     5D     5E     5F
919
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x40 | 0x00,
920
    // 60     61     62     63     64     65     66     67
921
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
922
    // 68     69     6A     6B     6C     6D     6E     6F
923
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
924
    // 70     71     72     73     74     75     76     77
925
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
926
    // 78     79     7A     7B     7C     7D     7E     7F
927
    0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80,
928
    // 80     81     82     83     84     85     86     87
929
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
930
    // 88     89     8A     8B     8C     8D     8E     8F
931
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
932
    // 90     91     92     93     94     95     96     97
933
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
934
    // 98     99     9A     9B     9C     9D     9E     9F
935
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
936
    // A0     A1     A2     A3     A4     A5     A6     A7
937
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
938
    // A8     A9     AA     AB     AC     AD     AE     AF
939
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
940
    // B0     B1     B2     B3     B4     B5     B6     B7
941
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
942
    // B8     B9     BA     BB     BC     BD     BE     BF
943
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
944
    // C0     C1     C2     C3     C4     C5     C6     C7
945
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
946
    // C8     C9     CA     CB     CC     CD     CE     CF
947
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
948
    // D0     D1     D2     D3     D4     D5     D6     D7
949
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
950
    // D8     D9     DA     DB     DC     DD     DE     DF
951
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
952
    // E0     E1     E2     E3     E4     E5     E6     E7
953
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
954
    // E8     E9     EA     EB     EC     ED     EE     EF
955
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
956
    // F0     F1     F2     F3     F4     F5     F6     F7
957
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
958
    // F8     F9     FA     FB     FC     FD     FE     FF
959
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
960
961
constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = {
962
    // 00     01     02     03     04     05     06     07
963
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
964
    // 08     09     0A     0B     0C     0D     0E     0F
965
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
966
    // 10     11     12     13     14     15     16     17
967
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
968
    // 18     19     1A     1B     1C     1D     1E     1F
969
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
970
    // 20     21     22     23     24     25     26     27
971
    0x00 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
972
    // 28     29     2A     2B     2C     2D     2E     2F
973
    0x01 | 0x02 | 0x00 | 0x08 | 0x10 | 0x00 | 0x00 | 0x80,
974
    // 30     31     32     33     34     35     36     37
975
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
976
    // 38     39     3A     3B     3C     3D     3E     3F
977
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
978
    // 40     41     42     43     44     45     46     47
979
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
980
    // 48     49     4A     4B     4C     4D     4E     4F
981
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
982
    // 50     51     52     53     54     55     56     57
983
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
984
    // 58     59     5A     5B     5C     5D     5E     5F
985
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
986
    // 60     61     62     63     64     65     66     67
987
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
988
    // 68     69     6A     6B     6C     6D     6E     6F
989
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
990
    // 70     71     72     73     74     75     76     77
991
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
992
    // 78     79     7A     7B     7C     7D     7E     7F
993
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
994
    // 80     81     82     83     84     85     86     87
995
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
996
    // 88     89     8A     8B     8C     8D     8E     8F
997
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
998
    // 90     91     92     93     94     95     96     97
999
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1000
    // 98     99     9A     9B     9C     9D     9E     9F
1001
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1002
    // A0     A1     A2     A3     A4     A5     A6     A7
1003
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1004
    // A8     A9     AA     AB     AC     AD     AE     AF
1005
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1006
    // B0     B1     B2     B3     B4     B5     B6     B7
1007
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1008
    // B8     B9     BA     BB     BC     BD     BE     BF
1009
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1010
    // C0     C1     C2     C3     C4     C5     C6     C7
1011
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1012
    // C8     C9     CA     CB     CC     CD     CE     CF
1013
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1014
    // D0     D1     D2     D3     D4     D5     D6     D7
1015
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1016
    // D8     D9     DA     DB     DC     DD     DE     DF
1017
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1018
    // E0     E1     E2     E3     E4     E5     E6     E7
1019
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1020
    // E8     E9     EA     EB     EC     ED     EE     EF
1021
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1022
    // F0     F1     F2     F3     F4     F5     F6     F7
1023
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1024
    // F8     F9     FA     FB     FC     FD     FE     FF
1025
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1026
1027
6.48M
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1028
6.48M
  return !!(a[i >> 3] & (1 << (i & 7)));
1029
6.48M
}
1030
1031
}  // namespace ada::character_sets
1032
1033
#endif  // ADA_CHARACTER_SETS_INL_H
1034
/* end file include/ada/character_sets-inl.h */
1035
/* begin file include/ada/checkers-inl.h */
1036
/**
1037
 * @file checkers-inl.h
1038
 * @brief Definitions for URL specific checkers used within Ada.
1039
 */
1040
#ifndef ADA_CHECKERS_INL_H
1041
#define ADA_CHECKERS_INL_H
1042
1043
#include <bit>
1044
#include <string_view>
1045
/* begin file include/ada/checkers.h */
1046
/**
1047
 * @file checkers.h
1048
 * @brief Declarations for URL specific checkers used within Ada.
1049
 */
1050
#ifndef ADA_CHECKERS_H
1051
#define ADA_CHECKERS_H
1052
1053
1054
#include <cstring>
1055
#include <string_view>
1056
1057
/**
1058
 * These functions are not part of our public API and may
1059
 * change at any time.
1060
 * @private
1061
 * @namespace ada::checkers
1062
 * @brief Includes the definitions for validation functions
1063
 */
1064
namespace ada::checkers {
1065
1066
/**
1067
 * @private
1068
 * Assuming that x is an ASCII letter, this function returns the lower case
1069
 * equivalent.
1070
 * @details More likely to be inlined by the compiler and constexpr.
1071
 */
1072
constexpr char to_lower(char x) noexcept;
1073
1074
/**
1075
 * @private
1076
 * Returns true if the character is an ASCII letter. Equivalent to std::isalpha
1077
 * but more likely to be inlined by the compiler.
1078
 *
1079
 * @attention std::isalpha is not constexpr generally.
1080
 */
1081
constexpr bool is_alpha(char x) noexcept;
1082
1083
/**
1084
 * @private
1085
 * Check whether a string starts with 0x or 0X. The function is only
1086
 * safe if input.size() >=2.
1087
 *
1088
 * @see has_hex_prefix
1089
 */
1090
constexpr bool has_hex_prefix_unsafe(std::string_view input);
1091
/**
1092
 * @private
1093
 * Check whether a string starts with 0x or 0X.
1094
 */
1095
constexpr bool has_hex_prefix(std::string_view input);
1096
1097
/**
1098
 * @private
1099
 * Check whether x is an ASCII digit. More likely to be inlined than
1100
 * std::isdigit.
1101
 */
1102
constexpr bool is_digit(char x) noexcept;
1103
1104
/**
1105
 * @private
1106
 * @details A string starts with a Windows drive letter if all of the following
1107
 * are true:
1108
 *
1109
 *   - its length is greater than or equal to 2
1110
 *   - its first two code points are a Windows drive letter
1111
 *   - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F
1112
 * (?), or U+0023 (#).
1113
 *
1114
 * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
1115
 */
1116
inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept;
1117
1118
/**
1119
 * @private
1120
 * @details A normalized Windows drive letter is a Windows drive letter of which
1121
 * the second code point is U+003A (:).
1122
 */
1123
inline constexpr bool is_normalized_windows_drive_letter(
1124
    std::string_view input) noexcept;
1125
1126
/**
1127
 * @private
1128
 * Returns true if an input is an ipv4 address. It is assumed that the string
1129
 * does not contain uppercase ASCII characters (the input should have been
1130
 * lowered cased before calling this function) and is not empty.
1131
 */
1132
ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept;
1133
1134
/**
1135
 * @private
1136
 * Returns a bitset. If the first bit is set, then at least one character needs
1137
 * percent encoding. If the second bit is set, a \\ is found. If the third bit
1138
 * is set then we have a dot. If the fourth bit is set, then we have a percent
1139
 * character.
1140
 */
1141
ada_really_inline constexpr uint8_t path_signature(
1142
    std::string_view input) noexcept;
1143
1144
/**
1145
 * @private
1146
 * Returns true if the length of the domain name and its labels are according to
1147
 * the specifications. The length of the domain must be 255 octets (253
1148
 * characters not including the last 2 which are the empty label reserved at the
1149
 * end). When the empty label is included (a dot at the end), the domain name
1150
 * can have 254 characters. The length of a label must be at least 1 and at most
1151
 * 63 characters.
1152
 * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034
1153
 * @see https://www.unicode.org/reports/tr46/#ToASCII
1154
 */
1155
ada_really_inline constexpr bool verify_dns_length(
1156
    std::string_view input) noexcept;
1157
1158
/**
1159
 * @private
1160
 * Fast-path parser for pure decimal IPv4 addresses (e.g., "192.168.1.1").
1161
 * Returns the packed 32-bit IPv4 address on success, or a value > 0xFFFFFFFF
1162
 * to indicate failure (caller should fall back to general parser).
1163
 * This is optimized for the common case where the input is a well-formed
1164
 * decimal IPv4 address with exactly 4 octets.
1165
 */
1166
ada_really_inline constexpr uint64_t try_parse_ipv4_fast(
1167
    std::string_view input) noexcept;
1168
1169
/**
1170
 * Sentinel value indicating try_parse_ipv4_fast() did not succeed.
1171
 * Any value > 0xFFFFFFFF indicates the fast path should not be used.
1172
 */
1173
constexpr uint64_t ipv4_fast_fail = uint64_t(1) << 32;
1174
1175
}  // namespace ada::checkers
1176
1177
#endif  // ADA_CHECKERS_H
1178
/* end file include/ada/checkers.h */
1179
1180
namespace ada::checkers {
1181
1182
9.29k
constexpr bool has_hex_prefix_unsafe(std::string_view input) {
1183
  // This is actually efficient code, see has_hex_prefix for the assembly.
1184
9.29k
  constexpr bool is_little_endian = std::endian::native == std::endian::little;
1185
9.29k
  constexpr uint16_t word0x = 0x7830;
1186
9.29k
  uint16_t two_first_bytes =
1187
9.29k
      static_cast<uint16_t>(input[0]) |
1188
9.29k
      static_cast<uint16_t>((static_cast<uint16_t>(input[1]) << 8));
1189
9.29k
  if constexpr (is_little_endian) {
1190
9.29k
    two_first_bytes |= 0x2000;
1191
  } else {
1192
    two_first_bytes |= 0x020;
1193
  }
1194
9.29k
  return two_first_bytes == word0x;
1195
9.29k
}
1196
1197
11.4k
constexpr bool has_hex_prefix(std::string_view input) {
1198
11.4k
  return input.size() >= 2 && has_hex_prefix_unsafe(input);
1199
11.4k
}
1200
1201
65.8k
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1202
1203
264k
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1204
1205
150k
constexpr bool is_alpha(char x) noexcept {
1206
150k
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1207
150k
}
1208
1209
38.0k
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1210
38.0k
  return input.size() >= 2 &&
1211
32.6k
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1212
4.07k
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1213
2.28k
                                  input[2] == '?' || input[2] == '#'));
1214
38.0k
}
1215
1216
constexpr bool is_normalized_windows_drive_letter(
1217
13.5k
    std::string_view input) noexcept {
1218
13.5k
  return input.size() >= 2 && (is_alpha(input[0]) && (input[1] == ':'));
1219
13.5k
}
1220
1221
ada_really_inline constexpr uint64_t try_parse_ipv4_fast(
1222
77.8k
    std::string_view input) noexcept {
1223
77.8k
  const char* p = input.data();
1224
77.8k
  const char* const pend = p + input.size();
1225
1226
77.8k
  uint32_t ipv4 = 0;
1227
1228
81.3k
  for (int i = 0; i < 4; ++i) {
1229
80.7k
    if (p == pend) {
1230
159
      return ipv4_fast_fail;
1231
159
    }
1232
1233
80.6k
    uint32_t val;
1234
80.6k
    char c = *p;
1235
80.6k
    if (c >= '0' && c <= '9') {
1236
15.4k
      val = c - '0';
1237
15.4k
      p++;
1238
65.1k
    } else {
1239
65.1k
      return ipv4_fast_fail;
1240
65.1k
    }
1241
1242
15.4k
    if (p < pend) {
1243
13.5k
      c = *p;
1244
13.5k
      if (c >= '0' && c <= '9') {
1245
5.44k
        if (val == 0) return ipv4_fast_fail;
1246
3.64k
        val = val * 10 + (c - '0');
1247
3.64k
        p++;
1248
3.64k
        if (p < pend) {
1249
3.23k
          c = *p;
1250
3.23k
          if (c >= '0' && c <= '9') {
1251
2.48k
            val = val * 10 + (c - '0');
1252
2.48k
            p++;
1253
2.48k
            if (val > 255) return ipv4_fast_fail;
1254
2.48k
          }
1255
3.23k
        }
1256
3.64k
      }
1257
13.5k
    }
1258
1259
12.1k
    ipv4 = (ipv4 << 8) | val;
1260
1261
12.1k
    if (i < 3) {
1262
11.5k
      if (p == pend || *p != '.') {
1263
8.66k
        return ipv4_fast_fail;
1264
8.66k
      }
1265
2.93k
      p++;
1266
2.93k
    }
1267
12.1k
  }
1268
1269
535
  if (p != pend) {
1270
430
    if (p == pend - 1 && *p == '.') {
1271
100
      return ipv4;
1272
100
    }
1273
330
    return ipv4_fast_fail;
1274
430
  }
1275
1276
105
  return ipv4;
1277
535
}
1278
1279
}  // namespace ada::checkers
1280
1281
#endif  // ADA_CHECKERS_INL_H
1282
/* end file include/ada/checkers-inl.h */
1283
/* begin file include/ada/log.h */
1284
/**
1285
 * @file log.h
1286
 * @brief Includes the definitions for logging.
1287
 * @private Excluded from docs through the doxygen file.
1288
 */
1289
#ifndef ADA_LOG_H
1290
#define ADA_LOG_H
1291
1292
// To enable logging, set ADA_LOGGING to 1:
1293
#ifndef ADA_LOGGING
1294
#define ADA_LOGGING 0
1295
#endif
1296
1297
#if ADA_LOGGING
1298
#include <iostream>
1299
#endif  // ADA_LOGGING
1300
1301
namespace ada {
1302
1303
/**
1304
 * Log a message. If you want to have no overhead when logging is disabled, use
1305
 * the ada_log macro.
1306
 * @private
1307
 */
1308
template <typename... Args>
1309
constexpr ada_really_inline void log([[maybe_unused]] Args... args) {
1310
#if ADA_LOGGING
1311
  ((std::cout << "ADA_LOG: ") << ... << args) << std::endl;
1312
#endif  // ADA_LOGGING
1313
}
1314
}  // namespace ada
1315
1316
#if ADA_LOGGING
1317
#ifndef ada_log
1318
#define ada_log(...)       \
1319
  do {                     \
1320
    ada::log(__VA_ARGS__); \
1321
  } while (0)
1322
#endif  // ada_log
1323
#else
1324
#define ada_log(...)
1325
#endif  // ADA_LOGGING
1326
1327
#endif  // ADA_LOG_H
1328
/* end file include/ada/log.h */
1329
/* begin file include/ada/encoding_type.h */
1330
/**
1331
 * @file encoding_type.h
1332
 * @brief Character encoding type definitions.
1333
 *
1334
 * Defines the encoding types supported for URL processing.
1335
 *
1336
 * @see https://encoding.spec.whatwg.org/
1337
 */
1338
#ifndef ADA_ENCODING_TYPE_H
1339
#define ADA_ENCODING_TYPE_H
1340
1341
#include <string>
1342
1343
namespace ada {
1344
1345
/**
1346
 * @brief Character encoding types for URL processing.
1347
 *
1348
 * Specifies the character encoding used for percent-decoding and other
1349
 * string operations. UTF-8 is the most commonly used encoding for URLs.
1350
 *
1351
 * @see https://encoding.spec.whatwg.org/#encodings
1352
 */
1353
enum class encoding_type {
1354
  UTF8,     /**< UTF-8 encoding (default for URLs) */
1355
  UTF_16LE, /**< UTF-16 Little Endian encoding */
1356
  UTF_16BE, /**< UTF-16 Big Endian encoding */
1357
};
1358
1359
/**
1360
 * Converts an encoding_type to its string representation.
1361
 * @param type The encoding type to convert.
1362
 * @return A string view of the encoding name.
1363
 */
1364
ada_warn_unused std::string_view to_string(encoding_type type);
1365
1366
}  // namespace ada
1367
1368
#endif  // ADA_ENCODING_TYPE_H
1369
/* end file include/ada/encoding_type.h */
1370
/* begin file include/ada/helpers.h */
1371
/**
1372
 * @file helpers.h
1373
 * @brief Definitions for helper functions used within Ada.
1374
 */
1375
#ifndef ADA_HELPERS_H
1376
#define ADA_HELPERS_H
1377
1378
/* begin file include/ada/url_base.h */
1379
/**
1380
 * @file url_base.h
1381
 * @brief Base class and common definitions for URL types.
1382
 *
1383
 * This file defines the `url_base` abstract base class from which both
1384
 * `ada::url` and `ada::url_aggregator` inherit. It also defines common
1385
 * enumerations like `url_host_type`.
1386
 */
1387
#ifndef ADA_URL_BASE_H
1388
#define ADA_URL_BASE_H
1389
1390
/* begin file include/ada/scheme.h */
1391
/**
1392
 * @file scheme.h
1393
 * @brief URL scheme type definitions and utilities.
1394
 *
1395
 * This header defines the URL scheme types (http, https, etc.) and provides
1396
 * functions to identify special schemes and their default ports according
1397
 * to the WHATWG URL Standard.
1398
 *
1399
 * @see https://url.spec.whatwg.org/#special-scheme
1400
 */
1401
#ifndef ADA_SCHEME_H
1402
#define ADA_SCHEME_H
1403
1404
1405
#include <string>
1406
1407
/**
1408
 * @namespace ada::scheme
1409
 * @brief URL scheme utilities and constants.
1410
 *
1411
 * Provides functions for working with URL schemes, including identification
1412
 * of special schemes and retrieval of default port numbers.
1413
 */
1414
namespace ada::scheme {
1415
1416
/**
1417
 * @brief Enumeration of URL scheme types.
1418
 *
1419
 * Special schemes have specific parsing rules and default ports.
1420
 * Using an enum allows efficient scheme comparisons without string operations.
1421
 *
1422
 * Default ports:
1423
 * - HTTP: 80
1424
 * - HTTPS: 443
1425
 * - WS: 80
1426
 * - WSS: 443
1427
 * - FTP: 21
1428
 * - FILE: (none)
1429
 */
1430
enum type : uint8_t {
1431
  HTTP = 0,        /**< http:// scheme (port 80) */
1432
  NOT_SPECIAL = 1, /**< Non-special scheme (no default port) */
1433
  HTTPS = 2,       /**< https:// scheme (port 443) */
1434
  WS = 3,          /**< ws:// WebSocket scheme (port 80) */
1435
  FTP = 4,         /**< ftp:// scheme (port 21) */
1436
  WSS = 5,         /**< wss:// secure WebSocket scheme (port 443) */
1437
  FILE = 6         /**< file:// scheme (no default port) */
1438
};
1439
1440
/**
1441
 * Checks if a scheme string is a special scheme.
1442
 * @param scheme The scheme string to check (e.g., "http", "https").
1443
 * @return `true` if the scheme is special, `false` otherwise.
1444
 * @see https://url.spec.whatwg.org/#special-scheme
1445
 */
1446
ada_really_inline constexpr bool is_special(std::string_view scheme);
1447
1448
/**
1449
 * Returns the default port for a special scheme string.
1450
 * @param scheme The scheme string (e.g., "http", "https").
1451
 * @return The default port number, or 0 if not a special scheme.
1452
 * @see https://url.spec.whatwg.org/#special-scheme
1453
 */
1454
constexpr uint16_t get_special_port(std::string_view scheme) noexcept;
1455
1456
/**
1457
 * Returns the default port for a scheme type.
1458
 * @param type The scheme type enum value.
1459
 * @return The default port number, or 0 if not applicable.
1460
 * @see https://url.spec.whatwg.org/#special-scheme
1461
 */
1462
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept;
1463
1464
/**
1465
 * Converts a scheme string to its type enum.
1466
 * @param scheme The scheme string to convert.
1467
 * @return The corresponding scheme type, or NOT_SPECIAL if not recognized.
1468
 */
1469
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept;
1470
1471
}  // namespace ada::scheme
1472
1473
#endif  // ADA_SCHEME_H
1474
/* end file include/ada/scheme.h */
1475
1476
#include <string>
1477
#include <string_view>
1478
1479
namespace ada {
1480
1481
/**
1482
 * @brief Enum representing the type of host in a URL.
1483
 *
1484
 * Used to distinguish between regular domain names, IPv4 addresses,
1485
 * and IPv6 addresses for proper parsing and serialization.
1486
 */
1487
enum url_host_type : uint8_t {
1488
  /** Regular domain name (e.g., "www.example.com") */
1489
  DEFAULT = 0,
1490
  /** IPv4 address (e.g., "127.0.0.1") */
1491
  IPV4 = 1,
1492
  /** IPv6 address (e.g., "[::1]" or "[2001:db8::1]") */
1493
  IPV6 = 2,
1494
};
1495
1496
/**
1497
 * @brief Abstract base class for URL representations.
1498
 *
1499
 * The `url_base` class provides the common interface and state shared by
1500
 * both `ada::url` and `ada::url_aggregator`. It contains basic URL attributes
1501
 * like validity status and scheme type, but delegates component storage and
1502
 * access to derived classes.
1503
 *
1504
 * @note This is an abstract class and cannot be instantiated directly.
1505
 *       Use `ada::url` or `ada::url_aggregator` instead.
1506
 *
1507
 * @see url
1508
 * @see url_aggregator
1509
 */
1510
struct url_base {
1511
161k
  virtual ~url_base() = default;
1512
1513
  /**
1514
   * Indicates whether the URL was successfully parsed.
1515
   * Set to `false` if parsing failed (e.g., invalid URL syntax).
1516
   */
1517
  bool is_valid{true};
1518
1519
  /**
1520
   * Indicates whether the URL has an opaque path (non-hierarchical).
1521
   * Opaque paths occur in non-special URLs like `mailto:` or `javascript:`.
1522
   */
1523
  bool has_opaque_path{false};
1524
1525
  /**
1526
   * The type of the URL's host (domain, IPv4, or IPv6).
1527
   */
1528
  url_host_type host_type = url_host_type::DEFAULT;
1529
1530
  /**
1531
   * @private
1532
   * Internal representation of the URL's scheme type.
1533
   */
1534
  ada::scheme::type type{ada::scheme::type::NOT_SPECIAL};
1535
1536
  /**
1537
   * Checks if the URL has a special scheme (http, https, ws, wss, ftp, file).
1538
   * Special schemes have specific parsing rules and default ports.
1539
   * @return `true` if the scheme is special, `false` otherwise.
1540
   */
1541
  [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept;
1542
1543
  /**
1544
   * Returns the URL's origin (scheme + host + port for special URLs).
1545
   * @return A newly allocated string containing the serialized origin.
1546
   * @see https://url.spec.whatwg.org/#concept-url-origin
1547
   */
1548
  [[nodiscard]] virtual std::string get_origin() const = 0;
1549
1550
  /**
1551
   * Validates whether the hostname is a valid domain according to RFC 1034.
1552
   * Checks that the domain and its labels have valid lengths.
1553
   * @return `true` if the domain is valid, `false` otherwise.
1554
   */
1555
  [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0;
1556
1557
  /**
1558
   * @private
1559
   * Returns the default port for special schemes (e.g., 443 for https).
1560
   * Returns 0 for file:// URLs or non-special schemes.
1561
   */
1562
  [[nodiscard]] inline uint16_t get_special_port() const noexcept;
1563
1564
  /**
1565
   * @private
1566
   * Returns the default port for the URL's scheme, or 0 if none.
1567
   */
1568
  [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept;
1569
1570
  /**
1571
   * @private
1572
   * Parses a port number from the input string.
1573
   * @param view The string containing the port to parse.
1574
   * @param check_trailing_content Whether to validate no trailing characters.
1575
   * @return Number of bytes consumed on success, 0 on failure.
1576
   */
1577
  virtual size_t parse_port(std::string_view view,
1578
                            bool check_trailing_content) = 0;
1579
1580
  /** @private */
1581
0
  virtual ada_really_inline size_t parse_port(std::string_view view) {
1582
0
    return this->parse_port(view, false);
1583
0
  }
1584
1585
  /**
1586
   * Returns a JSON string representation of this URL for debugging.
1587
   * @return A JSON-formatted string with URL information.
1588
   */
1589
  [[nodiscard]] virtual std::string to_string() const = 0;
1590
1591
  /** @private */
1592
  virtual inline void clear_pathname() = 0;
1593
1594
  /** @private */
1595
  virtual inline void clear_search() = 0;
1596
1597
  /** @private */
1598
  [[nodiscard]] virtual inline bool has_hash() const noexcept = 0;
1599
1600
  /** @private */
1601
  [[nodiscard]] virtual inline bool has_search() const noexcept = 0;
1602
1603
};  // url_base
1604
1605
}  // namespace ada
1606
1607
#endif
1608
/* end file include/ada/url_base.h */
1609
1610
#include <string>
1611
#include <string_view>
1612
#include <optional>
1613
1614
#if ADA_DEVELOPMENT_CHECKS
1615
#include <iostream>
1616
#endif  // ADA_DEVELOPMENT_CHECKS
1617
1618
/**
1619
 * These functions are not part of our public API and may
1620
 * change at any time.
1621
 *
1622
 * @private
1623
 * @namespace ada::helpers
1624
 * @brief Includes the definitions for helper functions
1625
 */
1626
namespace ada::helpers {
1627
1628
/**
1629
 * @private
1630
 */
1631
template <typename out_iter>
1632
void encode_json(std::string_view view, out_iter out);
1633
1634
/**
1635
 * @private
1636
 * This function is used to prune a fragment from a url, and returning the
1637
 * removed string if input has fragment.
1638
 *
1639
 * @details prune_hash seeks the first '#' and returns everything after it
1640
 * as a string_view, and modifies (in place) the input so that it points at
1641
 * everything before the '#'. If no '#' is found, the input is left unchanged
1642
 * and std::nullopt is returned.
1643
 *
1644
 * @attention The function is non-allocating and it does not throw.
1645
 * @returns Note that the returned string_view might be empty!
1646
 */
1647
ada_really_inline std::optional<std::string_view> prune_hash(
1648
    std::string_view& input) noexcept;
1649
1650
/**
1651
 * @private
1652
 * Defined by the URL specification, shorten a URLs paths.
1653
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1654
 * @returns Returns true if path is shortened.
1655
 */
1656
ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type);
1657
1658
/**
1659
 * @private
1660
 * Defined by the URL specification, shorten a URLs paths.
1661
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1662
 * @returns Returns true if path is shortened.
1663
 */
1664
ada_really_inline bool shorten_path(std::string_view& path,
1665
                                    ada::scheme::type type);
1666
1667
/**
1668
 * @private
1669
 *
1670
 * Parse the path from the provided input and append to the existing
1671
 * (possibly empty) path. The input cannot contain tabs and spaces: it
1672
 * is the user's responsibility to check.
1673
 *
1674
 * The input is expected to be UTF-8.
1675
 *
1676
 * @see https://url.spec.whatwg.org/
1677
 */
1678
ada_really_inline void parse_prepared_path(std::string_view input,
1679
                                           ada::scheme::type type,
1680
                                           std::string& path);
1681
1682
/**
1683
 * @private
1684
 * Remove and mutate all ASCII tab or newline characters from an input.
1685
 */
1686
ada_really_inline void remove_ascii_tab_or_newline(std::string& input);
1687
1688
/**
1689
 * @private
1690
 * Return the substring from input going from index pos to the end.
1691
 */
1692
ada_really_inline constexpr std::string_view substring(std::string_view input,
1693
                                                       size_t pos);
1694
1695
/**
1696
 * @private
1697
 * Returns true if the string_view points within the string.
1698
 */
1699
bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1700
1701
/**
1702
 * @private
1703
 * Return the substring from input going from index pos1 to the pos2 (non
1704
 * included). The length of the substring is pos2 - pos1.
1705
 */
1706
ada_really_inline constexpr std::string_view substring(std::string_view input,
1707
                                                       size_t pos1,
1708
418k
                                                       size_t pos2) {
1709
#if ADA_DEVELOPMENT_CHECKS
1710
  if (pos2 < pos1) {
1711
    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1712
              << std::endl;
1713
    abort();
1714
  }
1715
#endif
1716
418k
  return input.substr(pos1, pos2 - pos1);
1717
418k
}
1718
1719
/**
1720
 * @private
1721
 * Modify the string_view so that it has the new size pos, assuming that pos <=
1722
 * input.size(). This function cannot throw.
1723
 */
1724
ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1725
1726
/**
1727
 * @private
1728
 * Returns a host's delimiter location depending on the state of the instance,
1729
 * and whether a colon was found outside brackets. Used by the host parser.
1730
 */
1731
ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1732
    bool is_special, std::string_view& view) noexcept;
1733
1734
/**
1735
 * @private
1736
 * Removes leading and trailing C0 control and whitespace characters from
1737
 * string.
1738
 */
1739
void trim_c0_whitespace(std::string_view& input) noexcept;
1740
1741
/**
1742
 * @private
1743
 * @see
1744
 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1745
 */
1746
template <class url_type>
1747
ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url);
1748
1749
/**
1750
 * @private
1751
 * Finds the delimiter of a view in authority state.
1752
 */
1753
ada_really_inline size_t
1754
find_authority_delimiter_special(std::string_view view) noexcept;
1755
1756
/**
1757
 * @private
1758
 * Finds the delimiter of a view in authority state.
1759
 */
1760
ada_really_inline size_t
1761
find_authority_delimiter(std::string_view view) noexcept;
1762
1763
/**
1764
 * @private
1765
 */
1766
template <typename T, typename... Args>
1767
91.7k
inline void inner_concat(std::string& buffer, T t) {
1768
91.7k
  buffer.append(t);
1769
91.7k
}
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
1767
12.3k
inline void inner_concat(std::string& buffer, T t) {
1768
12.3k
  buffer.append(t);
1769
12.3k
}
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
1767
66.8k
inline void inner_concat(std::string& buffer, T t) {
1768
66.8k
  buffer.append(t);
1769
66.8k
}
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
1767
12.5k
inline void inner_concat(std::string& buffer, T t) {
1768
12.5k
  buffer.append(t);
1769
12.5k
}
1770
1771
/**
1772
 * @private
1773
 */
1774
template <typename T, typename... Args>
1775
113k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1776
113k
  buffer.append(t);
1777
113k
  return inner_concat(buffer, args...);
1778
113k
}
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
1775
12.3k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1776
12.3k
  buffer.append(t);
1777
12.3k
  return inner_concat(buffer, args...);
1778
12.3k
}
void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1775
9.47k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1776
9.47k
  buffer.append(t);
1777
9.47k
  return inner_concat(buffer, args...);
1778
9.47k
}
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
1775
61.3k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1776
61.3k
  buffer.append(t);
1777
61.3k
  return inner_concat(buffer, args...);
1778
61.3k
}
void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Line
Count
Source
1775
5.48k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1776
5.48k
  buffer.append(t);
1777
5.48k
  return inner_concat(buffer, args...);
1778
5.48k
}
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
1775
12.5k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1776
12.5k
  buffer.append(t);
1777
12.5k
  return inner_concat(buffer, args...);
1778
12.5k
}
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
1775
12.5k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1776
12.5k
  buffer.append(t);
1777
12.5k
  return inner_concat(buffer, args...);
1778
12.5k
}
1779
1780
/**
1781
 * @private
1782
 * Concatenate the arguments and return a string.
1783
 * @returns a string
1784
 */
1785
template <typename... Args>
1786
91.7k
std::string concat(Args... args) {
1787
91.7k
  std::string answer;
1788
91.7k
  inner_concat(answer, args...);
1789
91.7k
  return answer;
1790
91.7k
}
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
1786
2.85k
std::string concat(Args... args) {
1787
2.85k
  std::string answer;
1788
2.85k
  inner_concat(answer, args...);
1789
2.85k
  return answer;
1790
2.85k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1786
9.47k
std::string concat(Args... args) {
1787
9.47k
  std::string answer;
1788
9.47k
  inner_concat(answer, args...);
1789
9.47k
  return answer;
1790
9.47k
}
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
1786
61.3k
std::string concat(Args... args) {
1787
61.3k
  std::string answer;
1788
61.3k
  inner_concat(answer, args...);
1789
61.3k
  return answer;
1790
61.3k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Line
Count
Source
1786
5.48k
std::string concat(Args... args) {
1787
5.48k
  std::string answer;
1788
5.48k
  inner_concat(answer, args...);
1789
5.48k
  return answer;
1790
5.48k
}
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
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
1786
12.5k
std::string concat(Args... args) {
1787
12.5k
  std::string answer;
1788
12.5k
  inner_concat(answer, args...);
1789
12.5k
  return answer;
1790
12.5k
}
1791
1792
/**
1793
 * @private
1794
 * @return Number of leading zeroes.
1795
 */
1796
43.1k
inline int leading_zeroes(uint32_t input_num) noexcept {
1797
#if ADA_REGULAR_VISUAL_STUDIO
1798
  unsigned long leading_zero(0);
1799
  unsigned long in(input_num);
1800
  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
1801
#else
1802
43.1k
  return __builtin_clz(input_num);
1803
43.1k
#endif  // ADA_REGULAR_VISUAL_STUDIO
1804
43.1k
}
1805
1806
/**
1807
 * @private
1808
 * Counts the number of decimal digits necessary to represent x.
1809
 * faster than std::to_string(x).size().
1810
 * @return digit count
1811
 */
1812
1.10k
inline int fast_digit_count(uint32_t x) noexcept {
1813
1.10k
  auto int_log2 = [](uint32_t z) -> int {
1814
1.10k
    return 31 - ada::helpers::leading_zeroes(z | 1);
1815
1.10k
  };
1816
  // Compiles to very few instructions. Note that the
1817
  // table is static and thus effectively a constant.
1818
  // We leave it inside the function because it is meaningless
1819
  // outside of it (this comes at no performance cost).
1820
1.10k
  const static uint64_t table[] = {
1821
1.10k
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
1822
1.10k
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
1823
1.10k
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
1824
1.10k
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
1825
1.10k
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
1826
1.10k
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
1827
1.10k
      42949672960, 42949672960};
1828
1.10k
  return int((x + table[int_log2(x)]) >> 32);
1829
1.10k
}
1830
}  // namespace ada::helpers
1831
1832
#endif  // ADA_HELPERS_H
1833
/* end file include/ada/helpers.h */
1834
/* begin file include/ada/parser.h */
1835
/**
1836
 * @file parser.h
1837
 * @brief Low-level URL parsing functions.
1838
 *
1839
 * This header provides the internal URL parsing implementation. Most users
1840
 * should use `ada::parse()` from implementation.h instead of these functions
1841
 * directly.
1842
 *
1843
 * @see implementation.h for the recommended public API
1844
 */
1845
#ifndef ADA_PARSER_H
1846
#define ADA_PARSER_H
1847
1848
#include <string_view>
1849
#include <variant>
1850
1851
/* begin file include/ada/expected.h */
1852
/**
1853
 * @file expected.h
1854
 * @brief Definitions for std::expected
1855
 * @private Excluded from docs through the doxygen file.
1856
 */
1857
///
1858
// expected - An implementation of std::expected with extensions
1859
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
1860
//
1861
// Documentation available at http://tl.tartanllama.xyz/
1862
//
1863
// To the extent possible under law, the author(s) have dedicated all
1864
// copyright and related and neighboring rights to this software to the
1865
// public domain worldwide. This software is distributed without any warranty.
1866
//
1867
// You should have received a copy of the CC0 Public Domain Dedication
1868
// along with this software. If not, see
1869
// <http://creativecommons.org/publicdomain/zero/1.0/>.
1870
///
1871
1872
#ifndef TL_EXPECTED_HPP
1873
#define TL_EXPECTED_HPP
1874
1875
#define TL_EXPECTED_VERSION_MAJOR 1
1876
#define TL_EXPECTED_VERSION_MINOR 1
1877
#define TL_EXPECTED_VERSION_PATCH 0
1878
1879
#include <exception>
1880
#include <functional>
1881
#include <type_traits>
1882
#include <utility>
1883
1884
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
1885
#define TL_EXPECTED_EXCEPTIONS_ENABLED
1886
#endif
1887
1888
#if (defined(_MSC_VER) && _MSC_VER == 1900)
1889
#define TL_EXPECTED_MSVC2015
1890
#define TL_EXPECTED_MSVC2015_CONSTEXPR
1891
#else
1892
#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
1893
#endif
1894
1895
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
1896
     !defined(__clang__))
1897
#define TL_EXPECTED_GCC49
1898
#endif
1899
1900
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
1901
     !defined(__clang__))
1902
#define TL_EXPECTED_GCC54
1903
#endif
1904
1905
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
1906
     !defined(__clang__))
1907
#define TL_EXPECTED_GCC55
1908
#endif
1909
1910
#if !defined(TL_ASSERT)
1911
// can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
1912
#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
1913
#include <cassert>
1914
1.19M
#define TL_ASSERT(x) assert(x)
1915
#else
1916
#define TL_ASSERT(x)
1917
#endif
1918
#endif
1919
1920
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
1921
     !defined(__clang__))
1922
// GCC < 5 doesn't support overloading on const&& for member functions
1923
1924
#define TL_EXPECTED_NO_CONSTRR
1925
// GCC < 5 doesn't support some standard C++11 type traits
1926
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1927
  std::has_trivial_copy_constructor<T>
1928
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1929
  std::has_trivial_copy_assign<T>
1930
1931
// This one will be different for GCC 5.7 if it's ever supported
1932
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1933
  std::is_trivially_destructible<T>
1934
1935
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
1936
// std::vector for non-copyable types
1937
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
1938
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
1939
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
1940
namespace tl {
1941
namespace detail {
1942
template <class T>
1943
struct is_trivially_copy_constructible
1944
    : std::is_trivially_copy_constructible<T> {};
1945
#ifdef _GLIBCXX_VECTOR
1946
template <class T, class A>
1947
struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
1948
#endif
1949
}  // namespace detail
1950
}  // namespace tl
1951
#endif
1952
1953
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1954
  tl::detail::is_trivially_copy_constructible<T>
1955
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1956
  std::is_trivially_copy_assignable<T>
1957
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1958
  std::is_trivially_destructible<T>
1959
#else
1960
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1961
  std::is_trivially_copy_constructible<T>
1962
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1963
  std::is_trivially_copy_assignable<T>
1964
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1965
  std::is_trivially_destructible<T>
1966
#endif
1967
1968
#if __cplusplus > 201103L
1969
#define TL_EXPECTED_CXX14
1970
#endif
1971
1972
#ifdef TL_EXPECTED_GCC49
1973
#define TL_EXPECTED_GCC49_CONSTEXPR
1974
#else
1975
#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
1976
#endif
1977
1978
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
1979
     defined(TL_EXPECTED_GCC49))
1980
#define TL_EXPECTED_11_CONSTEXPR
1981
#else
1982
#define TL_EXPECTED_11_CONSTEXPR constexpr
1983
#endif
1984
1985
namespace tl {
1986
template <class T, class E>
1987
class expected;
1988
1989
#ifndef TL_MONOSTATE_INPLACE_MUTEX
1990
#define TL_MONOSTATE_INPLACE_MUTEX
1991
class monostate {};
1992
1993
struct in_place_t {
1994
  explicit in_place_t() = default;
1995
};
1996
static constexpr in_place_t in_place{};
1997
#endif
1998
1999
template <class E>
2000
class unexpected {
2001
 public:
2002
  static_assert(!std::is_same<E, void>::value, "E must not be void");
2003
2004
  unexpected() = delete;
2005
1.65k
  constexpr explicit unexpected(const E &e) : m_val(e) {}
2006
2007
55.1k
  constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
2008
2009
  template <class... Args, typename std::enable_if<std::is_constructible<
2010
                               E, Args &&...>::value>::type * = nullptr>
2011
  constexpr explicit unexpected(Args &&...args)
2012
6.89k
      : m_val(std::forward<Args>(args)...) {}
2013
  template <
2014
      class U, class... Args,
2015
      typename std::enable_if<std::is_constructible<
2016
          E, std::initializer_list<U> &, Args &&...>::value>::type * = nullptr>
2017
  constexpr explicit unexpected(std::initializer_list<U> l, Args &&...args)
2018
      : m_val(l, std::forward<Args>(args)...) {}
2019
2020
  constexpr const E &value() const & { return m_val; }
2021
40.1k
  TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
2022
  TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
2023
  constexpr const E &&value() const && { return std::move(m_val); }
2024
2025
 private:
2026
  E m_val;
2027
};
2028
2029
#ifdef __cpp_deduction_guides
2030
template <class E>
2031
unexpected(E) -> unexpected<E>;
2032
#endif
2033
2034
template <class E>
2035
constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2036
  return lhs.value() == rhs.value();
2037
}
2038
template <class E>
2039
constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2040
  return lhs.value() != rhs.value();
2041
}
2042
template <class E>
2043
constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2044
  return lhs.value() < rhs.value();
2045
}
2046
template <class E>
2047
constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2048
  return lhs.value() <= rhs.value();
2049
}
2050
template <class E>
2051
constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2052
  return lhs.value() > rhs.value();
2053
}
2054
template <class E>
2055
constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2056
  return lhs.value() >= rhs.value();
2057
}
2058
2059
template <class E>
2060
unexpected<typename std::decay<E>::type> make_unexpected(E &&e) {
2061
  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
2062
}
2063
2064
struct unexpect_t {
2065
  unexpect_t() = default;
2066
};
2067
static constexpr unexpect_t unexpect{};
2068
2069
namespace detail {
2070
template <typename E>
2071
0
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
2072
0
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2073
0
  throw std::forward<E>(e);
2074
#else
2075
  (void)e;
2076
#ifdef _MSC_VER
2077
  __assume(0);
2078
#else
2079
  __builtin_unreachable();
2080
#endif
2081
#endif
2082
0
}
2083
2084
#ifndef TL_TRAITS_MUTEX
2085
#define TL_TRAITS_MUTEX
2086
// C++14-style aliases for brevity
2087
template <class T>
2088
using remove_const_t = typename std::remove_const<T>::type;
2089
template <class T>
2090
using remove_reference_t = typename std::remove_reference<T>::type;
2091
template <class T>
2092
using decay_t = typename std::decay<T>::type;
2093
template <bool E, class T = void>
2094
using enable_if_t = typename std::enable_if<E, T>::type;
2095
template <bool B, class T, class F>
2096
using conditional_t = typename std::conditional<B, T, F>::type;
2097
2098
// std::conjunction from C++17
2099
template <class...>
2100
struct conjunction : std::true_type {};
2101
template <class B>
2102
struct conjunction<B> : B {};
2103
template <class B, class... Bs>
2104
struct conjunction<B, Bs...>
2105
    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2106
2107
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2108
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2109
#endif
2110
2111
// In C++11 mode, there's an issue in libc++'s std::mem_fn
2112
// which results in a hard-error when using it in a noexcept expression
2113
// in some cases. This is a check to workaround the common failing case.
2114
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2115
template <class T>
2116
struct is_pointer_to_non_const_member_func : std::false_type {};
2117
template <class T, class Ret, class... Args>
2118
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
2119
    : std::true_type {};
2120
template <class T, class Ret, class... Args>
2121
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
2122
    : std::true_type {};
2123
template <class T, class Ret, class... Args>
2124
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
2125
    : std::true_type {};
2126
template <class T, class Ret, class... Args>
2127
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
2128
    : std::true_type {};
2129
template <class T, class Ret, class... Args>
2130
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &>
2131
    : std::true_type {};
2132
template <class T, class Ret, class... Args>
2133
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &&>
2134
    : std::true_type {};
2135
2136
template <class T>
2137
struct is_const_or_const_ref : std::false_type {};
2138
template <class T>
2139
struct is_const_or_const_ref<T const &> : std::true_type {};
2140
template <class T>
2141
struct is_const_or_const_ref<T const> : std::true_type {};
2142
#endif
2143
2144
// std::invoke from C++17
2145
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2146
template <
2147
    typename Fn, typename... Args,
2148
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2149
    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2150
                             is_const_or_const_ref<Args...>::value)>,
2151
#endif
2152
    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2153
constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
2154
    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2155
    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2156
  return std::mem_fn(f)(std::forward<Args>(args)...);
2157
}
2158
2159
template <typename Fn, typename... Args,
2160
          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2161
constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
2162
    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2163
    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2164
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
2165
}
2166
2167
// std::invoke_result from C++17
2168
template <class F, class, class... Us>
2169
struct invoke_result_impl;
2170
2171
template <class F, class... Us>
2172
struct invoke_result_impl<
2173
    F,
2174
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2175
    Us...> {
2176
  using type =
2177
      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2178
};
2179
2180
template <class F, class... Us>
2181
using invoke_result = invoke_result_impl<F, void, Us...>;
2182
2183
template <class F, class... Us>
2184
using invoke_result_t = typename invoke_result<F, Us...>::type;
2185
2186
#if defined(_MSC_VER) && _MSC_VER <= 1900
2187
// TODO make a version which works with MSVC 2015
2188
template <class T, class U = T>
2189
struct is_swappable : std::true_type {};
2190
2191
template <class T, class U = T>
2192
struct is_nothrow_swappable : std::true_type {};
2193
#else
2194
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2195
namespace swap_adl_tests {
2196
// if swap ADL finds this then it would call std::swap otherwise (same
2197
// signature)
2198
struct tag {};
2199
2200
template <class T>
2201
tag swap(T &, T &);
2202
template <class T, std::size_t N>
2203
tag swap(T (&a)[N], T (&b)[N]);
2204
2205
// helper functions to test if an unqualified swap is possible, and if it
2206
// becomes std::swap
2207
template <class, class>
2208
std::false_type can_swap(...) noexcept(false);
2209
template <class T, class U,
2210
          class = decltype(swap(std::declval<T &>(), std::declval<U &>()))>
2211
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(),
2212
                                                    std::declval<U &>())));
2213
2214
template <class, class>
2215
std::false_type uses_std(...);
2216
template <class T, class U>
2217
std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag>
2218
uses_std(int);
2219
2220
template <class T>
2221
struct is_std_swap_noexcept
2222
    : std::integral_constant<bool,
2223
                             std::is_nothrow_move_constructible<T>::value &&
2224
                                 std::is_nothrow_move_assignable<T>::value> {};
2225
2226
template <class T, std::size_t N>
2227
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2228
2229
template <class T, class U>
2230
struct is_adl_swap_noexcept
2231
    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2232
}  // namespace swap_adl_tests
2233
2234
template <class T, class U = T>
2235
struct is_swappable
2236
    : std::integral_constant<
2237
          bool,
2238
          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2239
              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2240
               (std::is_move_assignable<T>::value &&
2241
                std::is_move_constructible<T>::value))> {};
2242
2243
template <class T, std::size_t N>
2244
struct is_swappable<T[N], T[N]>
2245
    : std::integral_constant<
2246
          bool,
2247
          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2248
              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2249
                   0))::value ||
2250
               is_swappable<T, T>::value)> {};
2251
2252
template <class T, class U = T>
2253
struct is_nothrow_swappable
2254
    : std::integral_constant<
2255
          bool,
2256
          is_swappable<T, U>::value &&
2257
              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2258
                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2259
               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2260
                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2261
#endif
2262
#endif
2263
2264
// Trait for checking if a type is a tl::expected
2265
template <class T>
2266
struct is_expected_impl : std::false_type {};
2267
template <class T, class E>
2268
struct is_expected_impl<expected<T, E>> : std::true_type {};
2269
template <class T>
2270
using is_expected = is_expected_impl<decay_t<T>>;
2271
2272
template <class T, class E, class U>
2273
using expected_enable_forward_value = detail::enable_if_t<
2274
    std::is_constructible<T, U &&>::value &&
2275
    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2276
    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2277
    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2278
2279
template <class T, class E, class U, class G, class UR, class GR>
2280
using expected_enable_from_other = detail::enable_if_t<
2281
    std::is_constructible<T, UR>::value &&
2282
    std::is_constructible<E, GR>::value &&
2283
    !std::is_constructible<T, expected<U, G> &>::value &&
2284
    !std::is_constructible<T, expected<U, G> &&>::value &&
2285
    !std::is_constructible<T, const expected<U, G> &>::value &&
2286
    !std::is_constructible<T, const expected<U, G> &&>::value &&
2287
    !std::is_convertible<expected<U, G> &, T>::value &&
2288
    !std::is_convertible<expected<U, G> &&, T>::value &&
2289
    !std::is_convertible<const expected<U, G> &, T>::value &&
2290
    !std::is_convertible<const expected<U, G> &&, T>::value>;
2291
2292
template <class T, class U>
2293
using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2294
2295
template <class T>
2296
using is_copy_constructible_or_void =
2297
    is_void_or<T, std::is_copy_constructible<T>>;
2298
2299
template <class T>
2300
using is_move_constructible_or_void =
2301
    is_void_or<T, std::is_move_constructible<T>>;
2302
2303
template <class T>
2304
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2305
2306
template <class T>
2307
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2308
2309
}  // namespace detail
2310
2311
namespace detail {
2312
struct no_init_t {};
2313
static constexpr no_init_t no_init{};
2314
2315
// Implements the storage of the values, and ensures that the destructor is
2316
// trivial if it can be.
2317
//
2318
// This specialization is for where neither `T` or `E` is trivially
2319
// destructible, so the destructors must be called on destruction of the
2320
// `expected`
2321
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2322
          bool = std::is_trivially_destructible<E>::value>
2323
struct expected_storage_base {
2324
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2325
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2326
2327
  template <class... Args,
2328
            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2329
                nullptr>
2330
  constexpr expected_storage_base(in_place_t, Args &&...args)
2331
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2332
2333
  template <class U, class... Args,
2334
            detail::enable_if_t<std::is_constructible<
2335
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2336
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2337
                                  Args &&...args)
2338
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2339
  template <class... Args,
2340
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2341
                nullptr>
2342
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2343
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2344
2345
  template <class U, class... Args,
2346
            detail::enable_if_t<std::is_constructible<
2347
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2348
  constexpr explicit expected_storage_base(unexpect_t,
2349
                                           std::initializer_list<U> il,
2350
                                           Args &&...args)
2351
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2352
2353
  ~expected_storage_base() {
2354
    if (m_has_val) {
2355
      m_val.~T();
2356
    } else {
2357
      m_unexpect.~unexpected<E>();
2358
    }
2359
  }
2360
  union {
2361
    T m_val;
2362
    unexpected<E> m_unexpect;
2363
    char m_no_init;
2364
  };
2365
  bool m_has_val;
2366
};
2367
2368
// This specialization is for when both `T` and `E` are trivially-destructible,
2369
// so the destructor of the `expected` can be trivial.
2370
template <class T, class E>
2371
struct expected_storage_base<T, E, true, true> {
2372
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2373
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2374
2375
  template <class... Args,
2376
            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2377
                nullptr>
2378
  constexpr expected_storage_base(in_place_t, Args &&...args)
2379
0
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS4_11char_traitsIcEEEELNS2_27url_search_params_iter_typeE0EEENS2_6errorsELb1ELb1EEC2IJSA_ETnPNS4_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS4_11char_traitsIcEEEELNS2_27url_search_params_iter_typeE1EEENS2_6errorsELb1ELb1EEC2IJSA_ETnPNS4_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada22url_search_params_iterINSt3__14pairINS4_17basic_string_viewIcNS4_11char_traitsIcEEEES9_EELNS2_27url_search_params_iter_typeE2EEENS2_6errorsELb1ELb1EEC2IJSC_ETnPNS4_9enable_ifIXsr3std16is_constructibleISC_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
2380
2381
  template <class U, class... Args,
2382
            detail::enable_if_t<std::is_constructible<
2383
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2384
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2385
                                  Args &&...args)
2386
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2387
  template <class... Args,
2388
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2389
                nullptr>
2390
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2391
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2392
2393
  template <class U, class... Args,
2394
            detail::enable_if_t<std::is_constructible<
2395
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2396
  constexpr explicit expected_storage_base(unexpect_t,
2397
                                           std::initializer_list<U> il,
2398
                                           Args &&...args)
2399
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2400
2401
  ~expected_storage_base() = default;
2402
  union {
2403
    T m_val;
2404
    unexpected<E> m_unexpect;
2405
    char m_no_init;
2406
  };
2407
  bool m_has_val;
2408
};
2409
2410
// T is trivial, E is not.
2411
template <class T, class E>
2412
struct expected_storage_base<T, E, true, false> {
2413
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2414
  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2415
      : m_no_init(), m_has_val(false) {}
2416
2417
  template <class... Args,
2418
            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2419
                nullptr>
2420
  constexpr expected_storage_base(in_place_t, Args &&...args)
2421
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2422
2423
  template <class U, class... Args,
2424
            detail::enable_if_t<std::is_constructible<
2425
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2426
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2427
                                  Args &&...args)
2428
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2429
  template <class... Args,
2430
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2431
                nullptr>
2432
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2433
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2434
2435
  template <class U, class... Args,
2436
            detail::enable_if_t<std::is_constructible<
2437
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2438
  constexpr explicit expected_storage_base(unexpect_t,
2439
                                           std::initializer_list<U> il,
2440
                                           Args &&...args)
2441
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2442
2443
  ~expected_storage_base() {
2444
    if (!m_has_val) {
2445
      m_unexpect.~unexpected<E>();
2446
    }
2447
  }
2448
2449
  union {
2450
    T m_val;
2451
    unexpected<E> m_unexpect;
2452
    char m_no_init;
2453
  };
2454
  bool m_has_val;
2455
};
2456
2457
// E is trivial, T is not.
2458
template <class T, class E>
2459
struct expected_storage_base<T, E, false, true> {
2460
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2461
0
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2462
2463
  template <class... Args,
2464
            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2465
                nullptr>
2466
  constexpr expected_storage_base(in_place_t, Args &&...args)
2467
257k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2467
15.4k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2467
31.5k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2467
4.21k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
2467
33.7k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJRA1_KcETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Line
Count
Source
2467
37.9k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJRS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Line
Count
Source
2467
47.7k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada17url_search_paramsENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEENS7_IS9_EEEEN3ada6errorsELb0ELb1EEC2IJSB_ETnPNS2_9enable_ifIXsr3std16is_constructibleISB_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
_ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJRS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESC_
Line
Count
Source
2467
6.77k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada16url_pattern_partENS2_9allocatorIS5_EEEENS4_6errorsELb0ELb1EEC2IJRS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
2467
37.9k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada21url_pattern_componentINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS6_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESE_
Line
Count
Source
2467
37.9k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada11url_patternINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS6_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESE_
Line
Count
Source
2467
4.21k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2468
2469
  template <class U, class... Args,
2470
            detail::enable_if_t<std::is_constructible<
2471
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2472
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2473
                                  Args &&...args)
2474
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2475
  template <class... Args,
2476
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2477
                nullptr>
2478
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2479
31.8k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2479
6.06k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2479
10.4k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2479
3.36k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Line
Count
Source
2479
1.37k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Line
Count
Source
2479
252
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada11url_patternINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS7_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESE_
Line
Count
Source
2479
7.02k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada16url_pattern_partENS2_9allocatorIS5_EEEENS4_6errorsELb0ELb1EEC2IJS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESF_
Line
Count
Source
2479
1.65k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada21url_pattern_componentINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS7_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESE_
Line
Count
Source
2479
1.65k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2480
2481
  template <class U, class... Args,
2482
            detail::enable_if_t<std::is_constructible<
2483
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2484
  constexpr explicit expected_storage_base(unexpect_t,
2485
                                           std::initializer_list<U> il,
2486
                                           Args &&...args)
2487
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2488
2489
289k
  ~expected_storage_base() {
2490
289k
    if (m_has_val) {
2491
257k
      m_val.~T();
2492
257k
    }
2493
289k
  }
tl::detail::expected_storage_base<ada::url, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2489
21.5k
  ~expected_storage_base() {
2490
21.5k
    if (m_has_val) {
2491
15.4k
      m_val.~T();
2492
15.4k
    }
2493
21.5k
  }
tl::detail::expected_storage_base<ada::url_aggregator, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2489
42.0k
  ~expected_storage_base() {
2490
42.0k
    if (m_has_val) {
2491
31.5k
      m_val.~T();
2492
31.5k
    }
2493
42.0k
  }
tl::detail::expected_storage_base<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2489
73.0k
  ~expected_storage_base() {
2490
73.0k
    if (m_has_val) {
2491
71.6k
      m_val.~T();
2492
71.6k
    }
2493
73.0k
  }
Unexecuted instantiation: tl::detail::expected_storage_base<ada::url_search_params, ada::errors, false, true>::~expected_storage_base()
Unexecuted instantiation: tl::detail::expected_storage_base<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors, false, true>::~expected_storage_base()
tl::detail::expected_storage_base<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2489
11.2k
  ~expected_storage_base() {
2490
11.2k
    if (m_has_val) {
2491
4.21k
      m_val.~T();
2492
4.21k
    }
2493
11.2k
  }
tl::detail::expected_storage_base<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2489
48.0k
  ~expected_storage_base() {
2490
48.0k
    if (m_has_val) {
2491
47.7k
      m_val.~T();
2492
47.7k
    }
2493
48.0k
  }
tl::detail::expected_storage_base<ada::url_pattern_init, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2489
14.3k
  ~expected_storage_base() {
2490
14.3k
    if (m_has_val) {
2491
10.9k
      m_val.~T();
2492
10.9k
    }
2493
14.3k
  }
tl::detail::expected_storage_base<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2489
39.5k
  ~expected_storage_base() {
2490
39.5k
    if (m_has_val) {
2491
37.9k
      m_val.~T();
2492
37.9k
    }
2493
39.5k
  }
tl::detail::expected_storage_base<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2489
39.5k
  ~expected_storage_base() {
2490
39.5k
    if (m_has_val) {
2491
37.9k
      m_val.~T();
2492
37.9k
    }
2493
39.5k
  }
2494
  union {
2495
    T m_val;
2496
    unexpected<E> m_unexpect;
2497
    char m_no_init;
2498
  };
2499
  bool m_has_val;
2500
};
2501
2502
// `T` is `void`, `E` is trivially-destructible
2503
template <class E>
2504
struct expected_storage_base<void, E, false, true> {
2505
#if __GNUC__ <= 5
2506
// no constexpr for GCC 4/5 bug
2507
#else
2508
  TL_EXPECTED_MSVC2015_CONSTEXPR
2509
#endif
2510
  expected_storage_base() : m_has_val(true) {}
2511
2512
  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2513
2514
  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2515
2516
  template <class... Args,
2517
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2518
                nullptr>
2519
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2520
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2521
2522
  template <class U, class... Args,
2523
            detail::enable_if_t<std::is_constructible<
2524
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2525
  constexpr explicit expected_storage_base(unexpect_t,
2526
                                           std::initializer_list<U> il,
2527
                                           Args &&...args)
2528
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2529
2530
  ~expected_storage_base() = default;
2531
  struct dummy {};
2532
  union {
2533
    unexpected<E> m_unexpect;
2534
    dummy m_val;
2535
  };
2536
  bool m_has_val;
2537
};
2538
2539
// `T` is `void`, `E` is not trivially-destructible
2540
template <class E>
2541
struct expected_storage_base<void, E, false, false> {
2542
  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2543
  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2544
2545
  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2546
2547
  template <class... Args,
2548
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2549
                nullptr>
2550
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2551
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2552
2553
  template <class U, class... Args,
2554
            detail::enable_if_t<std::is_constructible<
2555
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2556
  constexpr explicit expected_storage_base(unexpect_t,
2557
                                           std::initializer_list<U> il,
2558
                                           Args &&...args)
2559
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2560
2561
  ~expected_storage_base() {
2562
    if (!m_has_val) {
2563
      m_unexpect.~unexpected<E>();
2564
    }
2565
  }
2566
2567
  union {
2568
    unexpected<E> m_unexpect;
2569
    char m_dummy;
2570
  };
2571
  bool m_has_val;
2572
};
2573
2574
// This base class provides some handy member functions which can be used in
2575
// further derived classes
2576
template <class T, class E>
2577
struct expected_operations_base : expected_storage_base<T, E> {
2578
  using expected_storage_base<T, E>::expected_storage_base;
2579
2580
  template <class... Args>
2581
  void construct(Args &&...args) noexcept {
2582
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2583
    this->m_has_val = true;
2584
  }
2585
2586
  template <class Rhs>
2587
  // NOLINTNEXTLINE(bugprone-exception-escape)
2588
0
  void construct_with(Rhs &&rhs) noexcept {
2589
0
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2590
0
    this->m_has_val = true;
2591
0
  }
2592
2593
  template <class... Args>
2594
0
  void construct_error(Args &&...args) noexcept {
2595
0
    new (std::addressof(this->m_unexpect))
2596
0
        unexpected<E>(std::forward<Args>(args)...);
2597
0
    this->m_has_val = false;
2598
0
  }
2599
2600
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2601
2602
  // These assign overloads ensure that the most efficient assignment
2603
  // implementation is used while maintaining the strong exception guarantee.
2604
  // The problematic case is where rhs has a value, but *this does not.
2605
  //
2606
  // This overload handles the case where we can just copy-construct `T`
2607
  // directly into place without throwing.
2608
  template <class U = T,
2609
            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>
2610
                * = nullptr>
2611
  void assign(const expected_operations_base &rhs) noexcept {
2612
    if (!this->m_has_val && rhs.m_has_val) {
2613
      geterr().~unexpected<E>();
2614
      construct(rhs.get());
2615
    } else {
2616
      assign_common(rhs);
2617
    }
2618
  }
2619
2620
  // This overload handles the case where we can attempt to create a copy of
2621
  // `T`, then no-throw move it into place if the copy was successful.
2622
  template <class U = T,
2623
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2624
                                std::is_nothrow_move_constructible<U>::value>
2625
                * = nullptr>
2626
  void assign(const expected_operations_base &rhs) noexcept {
2627
    if (!this->m_has_val && rhs.m_has_val) {
2628
      T tmp = rhs.get();
2629
      geterr().~unexpected<E>();
2630
      construct(std::move(tmp));
2631
    } else {
2632
      assign_common(rhs);
2633
    }
2634
  }
2635
2636
  // This overload is the worst-case, where we have to move-construct the
2637
  // unexpected value into temporary storage, then try to copy the T into place.
2638
  // If the construction succeeds, then everything is fine, but if it throws,
2639
  // then we move the old unexpected value back into place before rethrowing the
2640
  // exception.
2641
  template <class U = T,
2642
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2643
                                !std::is_nothrow_move_constructible<U>::value>
2644
                * = nullptr>
2645
  void assign(const expected_operations_base &rhs) {
2646
    if (!this->m_has_val && rhs.m_has_val) {
2647
      auto tmp = std::move(geterr());
2648
      geterr().~unexpected<E>();
2649
2650
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2651
      try {
2652
        construct(rhs.get());
2653
      } catch (...) {
2654
        geterr() = std::move(tmp);
2655
        throw;
2656
      }
2657
#else
2658
      construct(rhs.get());
2659
#endif
2660
    } else {
2661
      assign_common(rhs);
2662
    }
2663
  }
2664
2665
  // These overloads do the same as above, but for rvalues
2666
  template <class U = T,
2667
            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>
2668
                * = nullptr>
2669
  void assign(expected_operations_base &&rhs) noexcept {
2670
    if (!this->m_has_val && rhs.m_has_val) {
2671
      geterr().~unexpected<E>();
2672
      construct(std::move(rhs).get());
2673
    } else {
2674
      assign_common(std::move(rhs));
2675
    }
2676
  }
2677
2678
  template <class U = T,
2679
            detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>
2680
                * = nullptr>
2681
  void assign(expected_operations_base &&rhs) {
2682
    if (!this->m_has_val && rhs.m_has_val) {
2683
      auto tmp = std::move(geterr());
2684
      geterr().~unexpected<E>();
2685
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2686
      try {
2687
        construct(std::move(rhs).get());
2688
      } catch (...) {
2689
        geterr() = std::move(tmp);
2690
        throw;
2691
      }
2692
#else
2693
      construct(std::move(rhs).get());
2694
#endif
2695
    } else {
2696
      assign_common(std::move(rhs));
2697
    }
2698
  }
2699
2700
#else
2701
2702
  // If exceptions are disabled then we can just copy-construct
2703
  void assign(const expected_operations_base &rhs) noexcept {
2704
    if (!this->m_has_val && rhs.m_has_val) {
2705
      geterr().~unexpected<E>();
2706
      construct(rhs.get());
2707
    } else {
2708
      assign_common(rhs);
2709
    }
2710
  }
2711
2712
  void assign(expected_operations_base &&rhs) noexcept {
2713
    if (!this->m_has_val && rhs.m_has_val) {
2714
      geterr().~unexpected<E>();
2715
      construct(std::move(rhs).get());
2716
    } else {
2717
      assign_common(std::move(rhs));
2718
    }
2719
  }
2720
2721
#endif
2722
2723
  // The common part of move/copy assigning
2724
  template <class Rhs>
2725
  void assign_common(Rhs &&rhs) {
2726
    if (this->m_has_val) {
2727
      if (rhs.m_has_val) {
2728
        get() = std::forward<Rhs>(rhs).get();
2729
      } else {
2730
        destroy_val();
2731
        construct_error(std::forward<Rhs>(rhs).geterr());
2732
      }
2733
    } else {
2734
      if (!rhs.m_has_val) {
2735
        geterr() = std::forward<Rhs>(rhs).geterr();
2736
      }
2737
    }
2738
  }
2739
2740
0
  bool has_value() const { return this->m_has_val; }
2741
2742
  TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; }
2743
0
  constexpr const T &get() const & { return this->m_val; }
2744
  TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); }
2745
#ifndef TL_EXPECTED_NO_CONSTRR
2746
  constexpr const T &&get() const && { return std::move(this->m_val); }
2747
#endif
2748
2749
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
2750
    return this->m_unexpect;
2751
  }
2752
0
  constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
2753
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
2754
    return std::move(this->m_unexpect);
2755
  }
2756
#ifndef TL_EXPECTED_NO_CONSTRR
2757
  constexpr const unexpected<E> &&geterr() const && {
2758
    return std::move(this->m_unexpect);
2759
  }
2760
#endif
2761
2762
  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2763
};
2764
2765
// This base class provides some handy member functions which can be used in
2766
// further derived classes
2767
template <class E>
2768
struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2769
  using expected_storage_base<void, E>::expected_storage_base;
2770
2771
  template <class... Args>
2772
  void construct() noexcept {
2773
    this->m_has_val = true;
2774
  }
2775
2776
  // This function doesn't use its argument, but needs it so that code in
2777
  // levels above this can work independently of whether T is void
2778
  template <class Rhs>
2779
  void construct_with(Rhs &&) noexcept {
2780
    this->m_has_val = true;
2781
  }
2782
2783
  template <class... Args>
2784
  void construct_error(Args &&...args) noexcept {
2785
    new (std::addressof(this->m_unexpect))
2786
        unexpected<E>(std::forward<Args>(args)...);
2787
    this->m_has_val = false;
2788
  }
2789
2790
  template <class Rhs>
2791
  void assign(Rhs &&rhs) noexcept {
2792
    if (!this->m_has_val) {
2793
      if (rhs.m_has_val) {
2794
        geterr().~unexpected<E>();
2795
        construct();
2796
      } else {
2797
        geterr() = std::forward<Rhs>(rhs).geterr();
2798
      }
2799
    } else {
2800
      if (!rhs.m_has_val) {
2801
        construct_error(std::forward<Rhs>(rhs).geterr());
2802
      }
2803
    }
2804
  }
2805
2806
  bool has_value() const { return this->m_has_val; }
2807
2808
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
2809
    return this->m_unexpect;
2810
  }
2811
  constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
2812
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
2813
    return std::move(this->m_unexpect);
2814
  }
2815
#ifndef TL_EXPECTED_NO_CONSTRR
2816
  constexpr const unexpected<E> &&geterr() const && {
2817
    return std::move(this->m_unexpect);
2818
  }
2819
#endif
2820
2821
  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
2822
    // no-op
2823
  }
2824
};
2825
2826
// This class manages conditionally having a trivial copy constructor
2827
// This specialization is for when T and E are trivially copy constructible
2828
template <class T, class E,
2829
          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(
2830
                                   T)>::value &&
2831
                 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
2832
struct expected_copy_base : expected_operations_base<T, E> {
2833
  using expected_operations_base<T, E>::expected_operations_base;
2834
};
2835
2836
// This specialization is for when T or E are not trivially copy constructible
2837
template <class T, class E>
2838
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
2839
  using expected_operations_base<T, E>::expected_operations_base;
2840
2841
  expected_copy_base() = default;
2842
  expected_copy_base(const expected_copy_base &rhs)
2843
0
      : expected_operations_base<T, E>(no_init) {
2844
0
    if (rhs.has_value()) {
2845
0
      this->construct_with(rhs);
2846
0
    } else {
2847
0
      this->construct_error(rhs.geterr());
2848
0
    }
2849
0
  }
2850
2851
  expected_copy_base(expected_copy_base &&rhs) = default;
2852
  expected_copy_base &operator=(const expected_copy_base &rhs) = default;
2853
  expected_copy_base &operator=(expected_copy_base &&rhs) = default;
2854
};
2855
2856
// This class manages conditionally having a trivial move constructor
2857
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
2858
// doesn't implement an analogue to std::is_trivially_move_constructible. We
2859
// have to make do with a non-trivial move constructor even if T is trivially
2860
// move constructible
2861
#ifndef TL_EXPECTED_GCC49
2862
template <class T, class E,
2863
          bool =
2864
              is_void_or<T, std::is_trivially_move_constructible<T>>::value &&
2865
              std::is_trivially_move_constructible<E>::value>
2866
struct expected_move_base : expected_copy_base<T, E> {
2867
  using expected_copy_base<T, E>::expected_copy_base;
2868
};
2869
#else
2870
template <class T, class E, bool = false>
2871
struct expected_move_base;
2872
#endif
2873
template <class T, class E>
2874
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
2875
  using expected_copy_base<T, E>::expected_copy_base;
2876
2877
  expected_move_base() = default;
2878
0
  expected_move_base(const expected_move_base &rhs) = default;
2879
2880
  expected_move_base(expected_move_base &&rhs) noexcept(
2881
      std::is_nothrow_move_constructible<T>::value)
2882
      : expected_copy_base<T, E>(no_init) {
2883
    if (rhs.has_value()) {
2884
      this->construct_with(std::move(rhs));
2885
    } else {
2886
      this->construct_error(std::move(rhs.geterr()));
2887
    }
2888
  }
2889
  expected_move_base &operator=(const expected_move_base &rhs) = default;
2890
  expected_move_base &operator=(expected_move_base &&rhs) = default;
2891
};
2892
2893
// This class manages conditionally having a trivial copy assignment operator
2894
template <
2895
    class T, class E,
2896
    bool =
2897
        is_void_or<
2898
            T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
2899
                           TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
2900
                           TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value &&
2901
        TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value &&
2902
        TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value &&
2903
        TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
2904
struct expected_copy_assign_base : expected_move_base<T, E> {
2905
  using expected_move_base<T, E>::expected_move_base;
2906
};
2907
2908
template <class T, class E>
2909
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
2910
  using expected_move_base<T, E>::expected_move_base;
2911
2912
  expected_copy_assign_base() = default;
2913
0
  expected_copy_assign_base(const expected_copy_assign_base &rhs) = default;
2914
2915
  expected_copy_assign_base(expected_copy_assign_base &&rhs) = default;
2916
  expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) {
2917
    this->assign(rhs);
2918
    return *this;
2919
  }
2920
  expected_copy_assign_base &operator=(expected_copy_assign_base &&rhs) =
2921
      default;
2922
};
2923
2924
// This class manages conditionally having a trivial move assignment operator
2925
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
2926
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
2927
// to make do with a non-trivial move assignment operator even if T is trivially
2928
// move assignable
2929
#ifndef TL_EXPECTED_GCC49
2930
template <
2931
    class T, class E,
2932
    bool = is_void_or<
2933
               T, conjunction<std::is_trivially_destructible<T>,
2934
                              std::is_trivially_move_constructible<T>,
2935
                              std::is_trivially_move_assignable<T>>>::value &&
2936
           std::is_trivially_destructible<E>::value &&
2937
           std::is_trivially_move_constructible<E>::value &&
2938
           std::is_trivially_move_assignable<E>::value>
2939
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
2940
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
2941
};
2942
#else
2943
template <class T, class E, bool = false>
2944
struct expected_move_assign_base;
2945
#endif
2946
2947
template <class T, class E>
2948
struct expected_move_assign_base<T, E, false>
2949
    : expected_copy_assign_base<T, E> {
2950
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
2951
2952
  expected_move_assign_base() = default;
2953
0
  expected_move_assign_base(const expected_move_assign_base &rhs) = default;
2954
2955
  expected_move_assign_base(expected_move_assign_base &&rhs) = default;
2956
2957
  expected_move_assign_base &operator=(const expected_move_assign_base &rhs) =
2958
      default;
2959
2960
  expected_move_assign_base &operator=(
2961
      expected_move_assign_base
2962
          &&rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
2963
                          std::is_nothrow_move_assignable<T>::value) {
2964
    this->assign(std::move(rhs));
2965
    return *this;
2966
  }
2967
};
2968
2969
// expected_delete_ctor_base will conditionally delete copy and move
2970
// constructors depending on whether T is copy/move constructible
2971
template <class T, class E,
2972
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
2973
                             std::is_copy_constructible<E>::value),
2974
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
2975
                             std::is_move_constructible<E>::value)>
2976
struct expected_delete_ctor_base {
2977
  expected_delete_ctor_base() = default;
2978
  expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
2979
  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
2980
  expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
2981
      default;
2982
  expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
2983
      default;
2984
};
2985
2986
template <class T, class E>
2987
struct expected_delete_ctor_base<T, E, true, false> {
2988
  expected_delete_ctor_base() = default;
2989
  expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
2990
  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
2991
  expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
2992
      default;
2993
  expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
2994
      default;
2995
};
2996
2997
template <class T, class E>
2998
struct expected_delete_ctor_base<T, E, false, true> {
2999
  expected_delete_ctor_base() = default;
3000
  expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
3001
  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
3002
  expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
3003
      default;
3004
  expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
3005
      default;
3006
};
3007
3008
template <class T, class E>
3009
struct expected_delete_ctor_base<T, E, false, false> {
3010
  expected_delete_ctor_base() = default;
3011
  expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
3012
  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
3013
  expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
3014
      default;
3015
  expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
3016
      default;
3017
};
3018
3019
// expected_delete_assign_base will conditionally delete copy and move
3020
// constructors depending on whether T and E are copy/move constructible +
3021
// assignable
3022
template <class T, class E,
3023
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3024
                             std::is_copy_constructible<E>::value &&
3025
                             is_copy_assignable_or_void<T>::value &&
3026
                             std::is_copy_assignable<E>::value),
3027
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3028
                             std::is_move_constructible<E>::value &&
3029
                             is_move_assignable_or_void<T>::value &&
3030
                             std::is_move_assignable<E>::value)>
3031
struct expected_delete_assign_base {
3032
  expected_delete_assign_base() = default;
3033
  expected_delete_assign_base(const expected_delete_assign_base &) = default;
3034
  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
3035
      default;
3036
  expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
3037
      default;
3038
  expected_delete_assign_base &operator=(
3039
      expected_delete_assign_base &&) noexcept = default;
3040
};
3041
3042
template <class T, class E>
3043
struct expected_delete_assign_base<T, E, true, false> {
3044
  expected_delete_assign_base() = default;
3045
  expected_delete_assign_base(const expected_delete_assign_base &) = default;
3046
  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
3047
      default;
3048
  expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
3049
      default;
3050
  expected_delete_assign_base &operator=(
3051
      expected_delete_assign_base &&) noexcept = delete;
3052
};
3053
3054
template <class T, class E>
3055
struct expected_delete_assign_base<T, E, false, true> {
3056
  expected_delete_assign_base() = default;
3057
  expected_delete_assign_base(const expected_delete_assign_base &) = default;
3058
  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
3059
      default;
3060
  expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
3061
      delete;
3062
  expected_delete_assign_base &operator=(
3063
      expected_delete_assign_base &&) noexcept = default;
3064
};
3065
3066
template <class T, class E>
3067
struct expected_delete_assign_base<T, E, false, false> {
3068
  expected_delete_assign_base() = default;
3069
  expected_delete_assign_base(const expected_delete_assign_base &) = default;
3070
  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
3071
      default;
3072
  expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
3073
      delete;
3074
  expected_delete_assign_base &operator=(
3075
      expected_delete_assign_base &&) noexcept = delete;
3076
};
3077
3078
// This is needed to be able to construct the expected_default_ctor_base which
3079
// follows, while still conditionally deleting the default constructor.
3080
struct default_constructor_tag {
3081
  explicit constexpr default_constructor_tag() = default;
3082
};
3083
3084
// expected_default_ctor_base will ensure that expected has a deleted default
3085
// consturctor if T is not default constructible.
3086
// This specialization is for when T is default constructible
3087
template <class T, class E,
3088
          bool Enable =
3089
              std::is_default_constructible<T>::value || std::is_void<T>::value>
3090
struct expected_default_ctor_base {
3091
  constexpr expected_default_ctor_base() noexcept = default;
3092
  constexpr expected_default_ctor_base(
3093
      expected_default_ctor_base const &) noexcept = default;
3094
  constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
3095
      default;
3096
  expected_default_ctor_base &operator=(
3097
      expected_default_ctor_base const &) noexcept = default;
3098
  expected_default_ctor_base &operator=(
3099
      expected_default_ctor_base &&) noexcept = default;
3100
3101
289k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3101
21.5k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_aggregator, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3101
42.0k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_pattern_init, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3101
14.3k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3101
73.0k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3101
48.0k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
tl::detail::expected_default_ctor_base<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3101
11.2k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3101
39.5k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3101
39.5k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3102
};
3103
3104
// This specialization is for when T is not default constructible
3105
template <class T, class E>
3106
struct expected_default_ctor_base<T, E, false> {
3107
  constexpr expected_default_ctor_base() noexcept = delete;
3108
  constexpr expected_default_ctor_base(
3109
      expected_default_ctor_base const &) noexcept = default;
3110
  constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
3111
      default;
3112
  expected_default_ctor_base &operator=(
3113
      expected_default_ctor_base const &) noexcept = default;
3114
  expected_default_ctor_base &operator=(
3115
      expected_default_ctor_base &&) noexcept = default;
3116
3117
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3118
};
3119
}  // namespace detail
3120
3121
template <class E>
3122
class bad_expected_access : public std::exception {
3123
 public:
3124
0
  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3125
3126
0
  virtual const char *what() const noexcept override {
3127
0
    return "Bad expected access";
3128
0
  }
3129
3130
  const E &error() const & { return m_val; }
3131
  E &error() & { return m_val; }
3132
  const E &&error() const && { return std::move(m_val); }
3133
  E &&error() && { return std::move(m_val); }
3134
3135
 private:
3136
  E m_val;
3137
};
3138
3139
/// An `expected<T, E>` object is an object that contains the storage for
3140
/// another object and manages the lifetime of this contained object `T`.
3141
/// Alternatively it could contain the storage for another unexpected object
3142
/// `E`. The contained object may not be initialized after the expected object
3143
/// has been initialized, and may not be destroyed before the expected object
3144
/// has been destroyed. The initialization state of the contained object is
3145
/// tracked by the expected object.
3146
template <class T, class E>
3147
class expected : private detail::expected_move_assign_base<T, E>,
3148
                 private detail::expected_delete_ctor_base<T, E>,
3149
                 private detail::expected_delete_assign_base<T, E>,
3150
                 private detail::expected_default_ctor_base<T, E> {
3151
  static_assert(!std::is_reference<T>::value, "T must not be a reference");
3152
  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3153
                "T must not be in_place_t");
3154
  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3155
                "T must not be unexpect_t");
3156
  static_assert(
3157
      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3158
      "T must not be unexpected<E>");
3159
  static_assert(!std::is_reference<E>::value, "E must not be a reference");
3160
3161
872k
  T *valptr() { return std::addressof(this->m_val); }
tl::expected<ada::url, ada::errors>::valptr()
Line
Count
Source
3161
199k
  T *valptr() { return std::addressof(this->m_val); }
tl::expected<ada::url_aggregator, ada::errors>::valptr()
Line
Count
Source
3161
483k
  T *valptr() { return std::addressof(this->m_val); }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::valptr()
tl::expected<ada::url_pattern_init, ada::errors>::valptr()
Line
Count
Source
3161
101k
  T *valptr() { return std::addressof(this->m_val); }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::valptr()
Line
Count
Source
3161
88.5k
  T *valptr() { return std::addressof(this->m_val); }
3162
  const T *valptr() const { return std::addressof(this->m_val); }
3163
  unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
3164
  const unexpected<E> *errptr() const {
3165
    return std::addressof(this->m_unexpect);
3166
  }
3167
3168
  template <class U = T,
3169
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3170
314k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3171
314k
    return this->m_val;
3172
314k
  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3170
3.04k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3171
3.04k
    return this->m_val;
3172
3.04k
  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3170
12.4k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3171
12.4k
    return this->m_val;
3172
12.4k
  }
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3170
71.6k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3171
71.6k
    return this->m_val;
3172
71.6k
  }
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEE3valIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3170
4.21k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3171
4.21k
    return this->m_val;
3172
4.21k
  }
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEE3valIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3170
47.7k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3171
47.7k
    return this->m_val;
3172
47.7k
  }
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3170
6.77k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3171
6.77k
    return this->m_val;
3172
6.77k
  }
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Line
Count
Source
3170
130k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3171
130k
    return this->m_val;
3172
130k
  }
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEE3valIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3170
37.9k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3171
37.9k
    return this->m_val;
3172
37.9k
  }
3173
8.30k
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::err()
Line
Count
Source
3173
1.37k
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::err()
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::err()
Line
Count
Source
3173
252
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
tl::expected<ada::url_pattern_init, ada::errors>::err()
Line
Count
Source
3173
3.36k
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::err()
Line
Count
Source
3173
1.65k
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::err()
Line
Count
Source
3173
1.65k
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
3174
3175
  template <class U = T,
3176
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3177
  constexpr const U &val() const {
3178
    return this->m_val;
3179
  }
3180
  constexpr const unexpected<E> &err() const { return this->m_unexpect; }
3181
3182
  using impl_base = detail::expected_move_assign_base<T, E>;
3183
  using ctor_base = detail::expected_default_ctor_base<T, E>;
3184
3185
 public:
3186
  typedef T value_type;
3187
  typedef E error_type;
3188
  typedef unexpected<E> unexpected_type;
3189
3190
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3191
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3192
  template <class F>
3193
  TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & {
3194
    return and_then_impl(*this, std::forward<F>(f));
3195
  }
3196
  template <class F>
3197
  TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && {
3198
    return and_then_impl(std::move(*this), std::forward<F>(f));
3199
  }
3200
  template <class F>
3201
  constexpr auto and_then(F &&f) const & {
3202
    return and_then_impl(*this, std::forward<F>(f));
3203
  }
3204
3205
#ifndef TL_EXPECTED_NO_CONSTRR
3206
  template <class F>
3207
  constexpr auto and_then(F &&f) const && {
3208
    return and_then_impl(std::move(*this), std::forward<F>(f));
3209
  }
3210
#endif
3211
3212
#else
3213
  template <class F>
3214
  TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl(
3215
      std::declval<expected &>(), std::forward<F>(f))) {
3216
    return and_then_impl(*this, std::forward<F>(f));
3217
  }
3218
  template <class F>
3219
  TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl(
3220
      std::declval<expected &&>(), std::forward<F>(f))) {
3221
    return and_then_impl(std::move(*this), std::forward<F>(f));
3222
  }
3223
  template <class F>
3224
  constexpr auto and_then(F &&f) const & -> decltype(and_then_impl(
3225
      std::declval<expected const &>(), std::forward<F>(f))) {
3226
    return and_then_impl(*this, std::forward<F>(f));
3227
  }
3228
3229
#ifndef TL_EXPECTED_NO_CONSTRR
3230
  template <class F>
3231
  constexpr auto and_then(F &&f) const && -> decltype(and_then_impl(
3232
      std::declval<expected const &&>(), std::forward<F>(f))) {
3233
    return and_then_impl(std::move(*this), std::forward<F>(f));
3234
  }
3235
#endif
3236
#endif
3237
3238
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3239
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3240
  template <class F>
3241
  TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
3242
    return expected_map_impl(*this, std::forward<F>(f));
3243
  }
3244
  template <class F>
3245
  TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && {
3246
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3247
  }
3248
  template <class F>
3249
  constexpr auto map(F &&f) const & {
3250
    return expected_map_impl(*this, std::forward<F>(f));
3251
  }
3252
  template <class F>
3253
  constexpr auto map(F &&f) const && {
3254
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3255
  }
3256
#else
3257
  template <class F>
3258
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3259
      std::declval<expected &>(), std::declval<F &&>()))
3260
  map(F &&f) & {
3261
    return expected_map_impl(*this, std::forward<F>(f));
3262
  }
3263
  template <class F>
3264
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3265
                                                      std::declval<F &&>()))
3266
  map(F &&f) && {
3267
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3268
  }
3269
  template <class F>
3270
  constexpr decltype(expected_map_impl(std::declval<const expected &>(),
3271
                                       std::declval<F &&>()))
3272
  map(F &&f) const & {
3273
    return expected_map_impl(*this, std::forward<F>(f));
3274
  }
3275
3276
#ifndef TL_EXPECTED_NO_CONSTRR
3277
  template <class F>
3278
  constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
3279
                                       std::declval<F &&>()))
3280
  map(F &&f) const && {
3281
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3282
  }
3283
#endif
3284
#endif
3285
3286
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3287
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3288
  template <class F>
3289
  TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & {
3290
    return expected_map_impl(*this, std::forward<F>(f));
3291
  }
3292
  template <class F>
3293
  TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && {
3294
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3295
  }
3296
  template <class F>
3297
  constexpr auto transform(F &&f) const & {
3298
    return expected_map_impl(*this, std::forward<F>(f));
3299
  }
3300
  template <class F>
3301
  constexpr auto transform(F &&f) const && {
3302
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3303
  }
3304
#else
3305
  template <class F>
3306
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3307
      std::declval<expected &>(), std::declval<F &&>()))
3308
  transform(F &&f) & {
3309
    return expected_map_impl(*this, std::forward<F>(f));
3310
  }
3311
  template <class F>
3312
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3313
                                                      std::declval<F &&>()))
3314
  transform(F &&f) && {
3315
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3316
  }
3317
  template <class F>
3318
  constexpr decltype(expected_map_impl(std::declval<const expected &>(),
3319
                                       std::declval<F &&>()))
3320
  transform(F &&f) const & {
3321
    return expected_map_impl(*this, std::forward<F>(f));
3322
  }
3323
3324
#ifndef TL_EXPECTED_NO_CONSTRR
3325
  template <class F>
3326
  constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
3327
                                       std::declval<F &&>()))
3328
  transform(F &&f) const && {
3329
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3330
  }
3331
#endif
3332
#endif
3333
3334
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3335
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3336
  template <class F>
3337
  TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & {
3338
    return map_error_impl(*this, std::forward<F>(f));
3339
  }
3340
  template <class F>
3341
  TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && {
3342
    return map_error_impl(std::move(*this), std::forward<F>(f));
3343
  }
3344
  template <class F>
3345
  constexpr auto map_error(F &&f) const & {
3346
    return map_error_impl(*this, std::forward<F>(f));
3347
  }
3348
  template <class F>
3349
  constexpr auto map_error(F &&f) const && {
3350
    return map_error_impl(std::move(*this), std::forward<F>(f));
3351
  }
3352
#else
3353
  template <class F>
3354
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
3355
                                                   std::declval<F &&>()))
3356
  map_error(F &&f) & {
3357
    return map_error_impl(*this, std::forward<F>(f));
3358
  }
3359
  template <class F>
3360
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
3361
                                                   std::declval<F &&>()))
3362
  map_error(F &&f) && {
3363
    return map_error_impl(std::move(*this), std::forward<F>(f));
3364
  }
3365
  template <class F>
3366
  constexpr decltype(map_error_impl(std::declval<const expected &>(),
3367
                                    std::declval<F &&>()))
3368
  map_error(F &&f) const & {
3369
    return map_error_impl(*this, std::forward<F>(f));
3370
  }
3371
3372
#ifndef TL_EXPECTED_NO_CONSTRR
3373
  template <class F>
3374
  constexpr decltype(map_error_impl(std::declval<const expected &&>(),
3375
                                    std::declval<F &&>()))
3376
  map_error(F &&f) const && {
3377
    return map_error_impl(std::move(*this), std::forward<F>(f));
3378
  }
3379
#endif
3380
#endif
3381
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3382
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3383
  template <class F>
3384
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & {
3385
    return map_error_impl(*this, std::forward<F>(f));
3386
  }
3387
  template <class F>
3388
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && {
3389
    return map_error_impl(std::move(*this), std::forward<F>(f));
3390
  }
3391
  template <class F>
3392
  constexpr auto transform_error(F &&f) const & {
3393
    return map_error_impl(*this, std::forward<F>(f));
3394
  }
3395
  template <class F>
3396
  constexpr auto transform_error(F &&f) const && {
3397
    return map_error_impl(std::move(*this), std::forward<F>(f));
3398
  }
3399
#else
3400
  template <class F>
3401
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
3402
                                                   std::declval<F &&>()))
3403
  transform_error(F &&f) & {
3404
    return map_error_impl(*this, std::forward<F>(f));
3405
  }
3406
  template <class F>
3407
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
3408
                                                   std::declval<F &&>()))
3409
  transform_error(F &&f) && {
3410
    return map_error_impl(std::move(*this), std::forward<F>(f));
3411
  }
3412
  template <class F>
3413
  constexpr decltype(map_error_impl(std::declval<const expected &>(),
3414
                                    std::declval<F &&>()))
3415
  transform_error(F &&f) const & {
3416
    return map_error_impl(*this, std::forward<F>(f));
3417
  }
3418
3419
#ifndef TL_EXPECTED_NO_CONSTRR
3420
  template <class F>
3421
  constexpr decltype(map_error_impl(std::declval<const expected &&>(),
3422
                                    std::declval<F &&>()))
3423
  transform_error(F &&f) const && {
3424
    return map_error_impl(std::move(*this), std::forward<F>(f));
3425
  }
3426
#endif
3427
#endif
3428
  template <class F>
3429
  expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
3430
    return or_else_impl(*this, std::forward<F>(f));
3431
  }
3432
3433
  template <class F>
3434
  expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && {
3435
    return or_else_impl(std::move(*this), std::forward<F>(f));
3436
  }
3437
3438
  template <class F>
3439
  expected constexpr or_else(F &&f) const & {
3440
    return or_else_impl(*this, std::forward<F>(f));
3441
  }
3442
3443
#ifndef TL_EXPECTED_NO_CONSTRR
3444
  template <class F>
3445
  expected constexpr or_else(F &&f) const && {
3446
    return or_else_impl(std::move(*this), std::forward<F>(f));
3447
  }
3448
#endif
3449
  constexpr expected() = default;
3450
0
  constexpr expected(const expected &rhs) = default;
3451
  constexpr expected(expected &&rhs) = default;
3452
  expected &operator=(const expected &rhs) = default;
3453
  expected &operator=(expected &&rhs) = default;
3454
3455
  template <class... Args,
3456
            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
3457
                nullptr>
3458
  constexpr expected(in_place_t, Args &&...args)
3459
257k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
257k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3459
15.4k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
15.4k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3459
31.5k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
31.5k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3459
4.21k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
4.21k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Line
Count
Source
3459
33.7k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
33.7k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA1_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Line
Count
Source
3459
37.9k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
37.9k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IJRS8_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
3459
47.7k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
47.7k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada17url_search_paramsENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS6_IS8_EEEEN3ada6errorsEEC2IJSA_ETnPNS1_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE0EEENS1_6errorsEEC2IJS9_ETnPNS3_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE1EEENS1_6errorsEEC2IJS9_ETnPNS3_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__14pairINS3_17basic_string_viewIcNS3_11char_traitsIcEEEES8_EELNS1_27url_search_params_iter_typeE2EEENS1_6errorsEEC2IJSB_ETnPNS3_9enable_ifIXsr3std16is_constructibleISB_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IJRS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
3459
6.77k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
6.77k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IJRS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Line
Count
Source
3459
37.9k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
37.9k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IJS5_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESD_
Line
Count
Source
3459
37.9k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
37.9k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IJS5_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESD_
Line
Count
Source
3459
4.21k
      : impl_base(in_place, std::forward<Args>(args)...),
3460
4.21k
        ctor_base(detail::default_constructor_tag{}) {}
3461
3462
  template <class U, class... Args,
3463
            detail::enable_if_t<std::is_constructible<
3464
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3465
  constexpr expected(in_place_t, std::initializer_list<U> il, Args &&...args)
3466
      : impl_base(in_place, il, std::forward<Args>(args)...),
3467
        ctor_base(detail::default_constructor_tag{}) {}
3468
3469
  template <class G = E,
3470
            detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
3471
                nullptr,
3472
            detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
3473
                nullptr>
3474
  explicit constexpr expected(const unexpected<G> &e)
3475
      : impl_base(unexpect, e.value()),
3476
        ctor_base(detail::default_constructor_tag{}) {}
3477
3478
  template <
3479
      class G = E,
3480
      detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
3481
          nullptr,
3482
      detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
3483
  constexpr expected(unexpected<G> const &e)
3484
      : impl_base(unexpect, e.value()),
3485
        ctor_base(detail::default_constructor_tag{}) {}
3486
3487
  template <
3488
      class G = E,
3489
      detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
3490
      detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
3491
  explicit constexpr expected(unexpected<G> &&e) noexcept(
3492
      std::is_nothrow_constructible<E, G &&>::value)
3493
      : impl_base(unexpect, std::move(e.value())),
3494
        ctor_base(detail::default_constructor_tag{}) {}
3495
3496
  template <
3497
      class G = E,
3498
      detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
3499
      detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
3500
  constexpr expected(unexpected<G> &&e) noexcept(
3501
      std::is_nothrow_constructible<E, G &&>::value)
3502
31.8k
      : impl_base(unexpect, std::move(e.value())),
3503
31.8k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3502
6.06k
      : impl_base(unexpect, std::move(e.value())),
3503
6.06k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3502
10.4k
      : impl_base(unexpect, std::move(e.value())),
3503
10.4k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3502
3.36k
      : impl_base(unexpect, std::move(e.value())),
3503
3.36k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Line
Count
Source
3502
1.37k
      : impl_base(unexpect, std::move(e.value())),
3503
1.37k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Line
Count
Source
3502
252
      : impl_base(unexpect, std::move(e.value())),
3503
252
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS6_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_OT_EE5valueEvE4typeELPv0ETnPNSA_IXsr3std14is_convertibleISC_S6_EE5valueEvE4typeELSG_0EEEONS_10unexpectedISB_EE
Line
Count
Source
3502
7.02k
      : impl_base(unexpect, std::move(e.value())),
3503
7.02k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IS8_TnPNS1_9enable_ifIXsr3std16is_constructibleIS8_OT_EE5valueEvE4typeELPv0ETnPNSB_IXsr3std14is_convertibleISD_S8_EE5valueEvE4typeELSH_0EEEONS_10unexpectedISC_EE
Line
Count
Source
3502
1.65k
      : impl_base(unexpect, std::move(e.value())),
3503
1.65k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS6_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_OT_EE5valueEvE4typeELPv0ETnPNSA_IXsr3std14is_convertibleISC_S6_EE5valueEvE4typeELSG_0EEEONS_10unexpectedISB_EE
Line
Count
Source
3502
1.65k
      : impl_base(unexpect, std::move(e.value())),
3503
1.65k
        ctor_base(detail::default_constructor_tag{}) {}
3504
3505
  template <class... Args,
3506
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
3507
                nullptr>
3508
  constexpr explicit expected(unexpect_t, Args &&...args)
3509
      : impl_base(unexpect, std::forward<Args>(args)...),
3510
        ctor_base(detail::default_constructor_tag{}) {}
3511
3512
  template <class U, class... Args,
3513
            detail::enable_if_t<std::is_constructible<
3514
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3515
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3516
                              Args &&...args)
3517
      : impl_base(unexpect, il, std::forward<Args>(args)...),
3518
        ctor_base(detail::default_constructor_tag{}) {}
3519
3520
  template <class U, class G,
3521
            detail::enable_if_t<!(std::is_convertible<U const &, T>::value &&
3522
                                  std::is_convertible<G const &, E>::value)> * =
3523
                nullptr,
3524
            detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
3525
                * = nullptr>
3526
  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
3527
      : ctor_base(detail::default_constructor_tag{}) {
3528
    if (rhs.has_value()) {
3529
      this->construct(*rhs);
3530
    } else {
3531
      this->construct_error(rhs.error());
3532
    }
3533
  }
3534
3535
  template <class U, class G,
3536
            detail::enable_if_t<(std::is_convertible<U const &, T>::value &&
3537
                                 std::is_convertible<G const &, E>::value)> * =
3538
                nullptr,
3539
            detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
3540
                * = nullptr>
3541
  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
3542
      : ctor_base(detail::default_constructor_tag{}) {
3543
    if (rhs.has_value()) {
3544
      this->construct(*rhs);
3545
    } else {
3546
      this->construct_error(rhs.error());
3547
    }
3548
  }
3549
3550
  template <
3551
      class U, class G,
3552
      detail::enable_if_t<!(std::is_convertible<U &&, T>::value &&
3553
                            std::is_convertible<G &&, E>::value)> * = nullptr,
3554
      detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
3555
  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
3556
      : ctor_base(detail::default_constructor_tag{}) {
3557
    if (rhs.has_value()) {
3558
      this->construct(std::move(*rhs));
3559
    } else {
3560
      this->construct_error(std::move(rhs.error()));
3561
    }
3562
  }
3563
3564
  template <
3565
      class U, class G,
3566
      detail::enable_if_t<(std::is_convertible<U &&, T>::value &&
3567
                           std::is_convertible<G &&, E>::value)> * = nullptr,
3568
      detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
3569
  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
3570
      : ctor_base(detail::default_constructor_tag{}) {
3571
    if (rhs.has_value()) {
3572
      this->construct(std::move(*rhs));
3573
    } else {
3574
      this->construct_error(std::move(rhs.error()));
3575
    }
3576
  }
3577
3578
  template <
3579
      class U = T,
3580
      detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
3581
      detail::expected_enable_forward_value<T, E, U> * = nullptr>
3582
  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
3583
      : expected(in_place, std::forward<U>(v)) {}
3584
3585
  template <
3586
      class U = T,
3587
      detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
3588
      detail::expected_enable_forward_value<T, E, U> * = nullptr>
3589
  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
3590
257k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3590
15.4k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3590
31.5k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3590
4.21k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS7_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS7_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESL_EE5valueEvE4typeELSI_0EEESE_
Line
Count
Source
3590
33.7k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IRA1_KcTnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleIS7_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SO_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESO_EE5valueEvE4typeELSL_0EEESH_
Line
Count
Source
3590
37.9k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IRS8_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S8_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS8_SF_EE5valuentsr3std7is_sameINS1_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SM_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESM_EE5valueEvE4typeELSJ_0EEESF_
Line
Count
Source
3590
47.7k
      : expected(in_place, std::forward<U>(v)) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada17url_search_paramsENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS6_IS8_EEEEN3ada6errorsEEC2ISA_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_SA_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleISA_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISD_SO_EE5valuentsr3std7is_sameINS_10unexpectedISC_EESO_EE5valueEvE4typeELSL_0EEESH_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE0EEENS1_6errorsEEC2IS9_TnPNS3_9enable_ifIXsr3std14is_convertibleIOT_S9_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS9_SF_EE5valuentsr3std7is_sameINS3_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISB_SM_EE5valuentsr3std7is_sameINS_10unexpectedISA_EESM_EE5valueEvE4typeELSJ_0EEESF_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE1EEENS1_6errorsEEC2IS9_TnPNS3_9enable_ifIXsr3std14is_convertibleIOT_S9_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS9_SF_EE5valuentsr3std7is_sameINS3_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISB_SM_EE5valuentsr3std7is_sameINS_10unexpectedISA_EESM_EE5valueEvE4typeELSJ_0EEESF_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__14pairINS3_17basic_string_viewIcNS3_11char_traitsIcEEEES8_EELNS1_27url_search_params_iter_typeE2EEENS1_6errorsEEC2ISB_TnPNS3_9enable_ifIXsr3std14is_convertibleIOT_SB_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleISB_SH_EE5valuentsr3std7is_sameINS3_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISD_SO_EE5valuentsr3std7is_sameINS_10unexpectedISC_EESO_EE5valueEvE4typeELSL_0EEESH_
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IRS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS8_IXaaaaaasr3std16is_constructibleIS2_SA_EE5valuentsr3std7is_sameINS7_5decayIS9_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SH_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESH_EE5valueEvE4typeELSE_0EEESA_
Line
Count
Source
3590
6.77k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IRS7_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS7_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS9_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS8_EESL_EE5valueEvE4typeELSI_0EEESE_
Line
Count
Source
3590
37.9k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS5_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S5_EE5valueEvE4typeELPv0ETnPNSA_IXaaaaaasr3std16is_constructibleIS5_SC_EE5valuentsr3std7is_sameINS9_5decayISB_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS7_SJ_EE5valuentsr3std7is_sameINS_10unexpectedIS6_EESJ_EE5valueEvE4typeELSG_0EEESC_
Line
Count
Source
3590
37.9k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS5_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S5_EE5valueEvE4typeELPv0ETnPNSA_IXaaaaaasr3std16is_constructibleIS5_SC_EE5valuentsr3std7is_sameINS9_5decayISB_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS7_SJ_EE5valuentsr3std7is_sameINS_10unexpectedIS6_EESJ_EE5valueEvE4typeELSG_0EEESC_
Line
Count
Source
3590
4.21k
      : expected(in_place, std::forward<U>(v)) {}
3591
3592
  template <
3593
      class U = T, class G = T,
3594
      detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * =
3595
          nullptr,
3596
      detail::enable_if_t<!std::is_void<G>::value> * = nullptr,
3597
      detail::enable_if_t<
3598
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3599
           !detail::conjunction<std::is_scalar<T>,
3600
                                std::is_same<T, detail::decay_t<U>>>::value &&
3601
           std::is_constructible<T, U>::value &&
3602
           std::is_assignable<G &, U>::value &&
3603
           std::is_nothrow_move_constructible<E>::value)> * = nullptr>
3604
  expected &operator=(U &&v) {
3605
    if (has_value()) {
3606
      val() = std::forward<U>(v);
3607
    } else {
3608
      err().~unexpected<E>();
3609
      ::new (valptr()) T(std::forward<U>(v));
3610
      this->m_has_val = true;
3611
    }
3612
3613
    return *this;
3614
  }
3615
3616
  template <
3617
      class U = T, class G = T,
3618
      detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * =
3619
          nullptr,
3620
      detail::enable_if_t<!std::is_void<U>::value> * = nullptr,
3621
      detail::enable_if_t<
3622
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3623
           !detail::conjunction<std::is_scalar<T>,
3624
                                std::is_same<T, detail::decay_t<U>>>::value &&
3625
           std::is_constructible<T, U>::value &&
3626
           std::is_assignable<G &, U>::value &&
3627
           std::is_nothrow_move_constructible<E>::value)> * = nullptr>
3628
  expected &operator=(U &&v) {
3629
    if (has_value()) {
3630
      val() = std::forward<U>(v);
3631
    } else {
3632
      auto tmp = std::move(err());
3633
      err().~unexpected<E>();
3634
3635
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3636
      try {
3637
        ::new (valptr()) T(std::forward<U>(v));
3638
        this->m_has_val = true;
3639
      } catch (...) {
3640
        err() = std::move(tmp);
3641
        throw;
3642
      }
3643
#else
3644
      ::new (valptr()) T(std::forward<U>(v));
3645
      this->m_has_val = true;
3646
#endif
3647
    }
3648
3649
    return *this;
3650
  }
3651
3652
  template <class G = E,
3653
            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3654
                                std::is_assignable<G &, G>::value> * = nullptr>
3655
  expected &operator=(const unexpected<G> &rhs) {
3656
    if (!has_value()) {
3657
      err() = rhs;
3658
    } else {
3659
      this->destroy_val();
3660
      ::new (errptr()) unexpected<E>(rhs);
3661
      this->m_has_val = false;
3662
    }
3663
3664
    return *this;
3665
  }
3666
3667
  template <class G = E,
3668
            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3669
                                std::is_move_assignable<G>::value> * = nullptr>
3670
  expected &operator=(unexpected<G> &&rhs) noexcept {
3671
    if (!has_value()) {
3672
      err() = std::move(rhs);
3673
    } else {
3674
      this->destroy_val();
3675
      ::new (errptr()) unexpected<E>(std::move(rhs));
3676
      this->m_has_val = false;
3677
    }
3678
3679
    return *this;
3680
  }
3681
3682
  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3683
                               T, Args &&...>::value> * = nullptr>
3684
  void emplace(Args &&...args) {
3685
    if (has_value()) {
3686
      val().~T();
3687
    } else {
3688
      err().~unexpected<E>();
3689
      this->m_has_val = true;
3690
    }
3691
    ::new (valptr()) T(std::forward<Args>(args)...);
3692
  }
3693
3694
  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3695
                               T, Args &&...>::value> * = nullptr>
3696
  void emplace(Args &&...args) {
3697
    if (has_value()) {
3698
      val().~T();
3699
      ::new (valptr()) T(std::forward<Args>(args)...);
3700
    } else {
3701
      auto tmp = std::move(err());
3702
      err().~unexpected<E>();
3703
3704
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3705
      try {
3706
        ::new (valptr()) T(std::forward<Args>(args)...);
3707
        this->m_has_val = true;
3708
      } catch (...) {
3709
        err() = std::move(tmp);
3710
        throw;
3711
      }
3712
#else
3713
      ::new (valptr()) T(std::forward<Args>(args)...);
3714
      this->m_has_val = true;
3715
#endif
3716
    }
3717
  }
3718
3719
  template <class U, class... Args,
3720
            detail::enable_if_t<std::is_nothrow_constructible<
3721
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3722
  void emplace(std::initializer_list<U> il, Args &&...args) {
3723
    if (has_value()) {
3724
      T t(il, std::forward<Args>(args)...);
3725
      val() = std::move(t);
3726
    } else {
3727
      err().~unexpected<E>();
3728
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3729
      this->m_has_val = true;
3730
    }
3731
  }
3732
3733
  template <class U, class... Args,
3734
            detail::enable_if_t<!std::is_nothrow_constructible<
3735
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3736
  void emplace(std::initializer_list<U> il, Args &&...args) {
3737
    if (has_value()) {
3738
      T t(il, std::forward<Args>(args)...);
3739
      val() = std::move(t);
3740
    } else {
3741
      auto tmp = std::move(err());
3742
      err().~unexpected<E>();
3743
3744
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3745
      try {
3746
        ::new (valptr()) T(il, std::forward<Args>(args)...);
3747
        this->m_has_val = true;
3748
      } catch (...) {
3749
        err() = std::move(tmp);
3750
        throw;
3751
      }
3752
#else
3753
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3754
      this->m_has_val = true;
3755
#endif
3756
    }
3757
  }
3758
3759
 private:
3760
  using t_is_void = std::true_type;
3761
  using t_is_not_void = std::false_type;
3762
  using t_is_nothrow_move_constructible = std::true_type;
3763
  using move_constructing_t_can_throw = std::false_type;
3764
  using e_is_nothrow_move_constructible = std::true_type;
3765
  using move_constructing_e_can_throw = std::false_type;
3766
3767
  void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept {
3768
    // swapping void is a no-op
3769
  }
3770
3771
  void swap_where_both_have_value(expected &rhs, t_is_not_void) {
3772
    using std::swap;
3773
    swap(val(), rhs.val());
3774
  }
3775
3776
  void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept(
3777
      std::is_nothrow_move_constructible<E>::value) {
3778
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3779
    rhs.err().~unexpected_type();
3780
    std::swap(this->m_has_val, rhs.m_has_val);
3781
  }
3782
3783
  void swap_where_only_one_has_value(expected &rhs, t_is_not_void) {
3784
    swap_where_only_one_has_value_and_t_is_not_void(
3785
        rhs, typename std::is_nothrow_move_constructible<T>::type{},
3786
        typename std::is_nothrow_move_constructible<E>::type{});
3787
  }
3788
3789
  void swap_where_only_one_has_value_and_t_is_not_void(
3790
      expected &rhs, t_is_nothrow_move_constructible,
3791
      e_is_nothrow_move_constructible) noexcept {
3792
    auto temp = std::move(val());
3793
    val().~T();
3794
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3795
    rhs.err().~unexpected_type();
3796
    ::new (rhs.valptr()) T(std::move(temp));
3797
    std::swap(this->m_has_val, rhs.m_has_val);
3798
  }
3799
3800
  void swap_where_only_one_has_value_and_t_is_not_void(
3801
      expected &rhs, t_is_nothrow_move_constructible,
3802
      move_constructing_e_can_throw) {
3803
    auto temp = std::move(val());
3804
    val().~T();
3805
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3806
    try {
3807
      ::new (errptr()) unexpected_type(std::move(rhs.err()));
3808
      rhs.err().~unexpected_type();
3809
      ::new (rhs.valptr()) T(std::move(temp));
3810
      std::swap(this->m_has_val, rhs.m_has_val);
3811
    } catch (...) {
3812
      val() = std::move(temp);
3813
      throw;
3814
    }
3815
#else
3816
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3817
    rhs.err().~unexpected_type();
3818
    ::new (rhs.valptr()) T(std::move(temp));
3819
    std::swap(this->m_has_val, rhs.m_has_val);
3820
#endif
3821
  }
3822
3823
  void swap_where_only_one_has_value_and_t_is_not_void(
3824
      expected &rhs, move_constructing_t_can_throw,
3825
      e_is_nothrow_move_constructible) {
3826
    auto temp = std::move(rhs.err());
3827
    rhs.err().~unexpected_type();
3828
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3829
    try {
3830
      ::new (rhs.valptr()) T(std::move(val()));
3831
      val().~T();
3832
      ::new (errptr()) unexpected_type(std::move(temp));
3833
      std::swap(this->m_has_val, rhs.m_has_val);
3834
    } catch (...) {
3835
      rhs.err() = std::move(temp);
3836
      throw;
3837
    }
3838
#else
3839
    ::new (rhs.valptr()) T(std::move(val()));
3840
    val().~T();
3841
    ::new (errptr()) unexpected_type(std::move(temp));
3842
    std::swap(this->m_has_val, rhs.m_has_val);
3843
#endif
3844
  }
3845
3846
 public:
3847
  template <class OT = T, class OE = E>
3848
  detail::enable_if_t<detail::is_swappable<OT>::value &&
3849
                      detail::is_swappable<OE>::value &&
3850
                      (std::is_nothrow_move_constructible<OT>::value ||
3851
                       std::is_nothrow_move_constructible<OE>::value)>
3852
  swap(expected &rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
3853
                               detail::is_nothrow_swappable<T>::value &&
3854
                               std::is_nothrow_move_constructible<E>::value &&
3855
                               detail::is_nothrow_swappable<E>::value) {
3856
    if (has_value() && rhs.has_value()) {
3857
      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
3858
    } else if (!has_value() && rhs.has_value()) {
3859
      rhs.swap(*this);
3860
    } else if (has_value()) {
3861
      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
3862
    } else {
3863
      using std::swap;
3864
      swap(err(), rhs.err());
3865
    }
3866
  }
3867
3868
  constexpr const T *operator->() const {
3869
    TL_ASSERT(has_value());
3870
    return valptr();
3871
  }
3872
872k
  TL_EXPECTED_11_CONSTEXPR T *operator->() {
3873
872k
    TL_ASSERT(has_value());
3874
872k
    return valptr();
3875
872k
  }
tl::expected<ada::url, ada::errors>::operator->()
Line
Count
Source
3872
199k
  TL_EXPECTED_11_CONSTEXPR T *operator->() {
3873
199k
    TL_ASSERT(has_value());
3874
199k
    return valptr();
3875
199k
  }
tl::expected<ada::url_aggregator, ada::errors>::operator->()
Line
Count
Source
3872
483k
  TL_EXPECTED_11_CONSTEXPR T *operator->() {
3873
483k
    TL_ASSERT(has_value());
3874
483k
    return valptr();
3875
483k
  }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::operator->()
tl::expected<ada::url_pattern_init, ada::errors>::operator->()
Line
Count
Source
3872
101k
  TL_EXPECTED_11_CONSTEXPR T *operator->() {
3873
101k
    TL_ASSERT(has_value());
3874
101k
    return valptr();
3875
101k
  }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::operator->()
Line
Count
Source
3872
88.5k
  TL_EXPECTED_11_CONSTEXPR T *operator->() {
3873
88.5k
    TL_ASSERT(has_value());
3874
88.5k
    return valptr();
3875
88.5k
  }
3876
3877
  template <class U = T,
3878
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3879
  constexpr const U &operator*() const & {
3880
    TL_ASSERT(has_value());
3881
    return val();
3882
  }
3883
  template <class U = T,
3884
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3885
314k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3886
314k
    TL_ASSERT(has_value());
3887
314k
    return val();
3888
314k
  }
_ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3885
3.04k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3886
3.04k
    TL_ASSERT(has_value());
3887
3.04k
    return val();
3888
3.04k
  }
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3885
12.4k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3886
12.4k
    TL_ASSERT(has_value());
3887
12.4k
    return val();
3888
12.4k
  }
_ZNR2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3885
71.6k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3886
71.6k
    TL_ASSERT(has_value());
3887
71.6k
    return val();
3888
71.6k
  }
_ZNR2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEdeIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3885
4.21k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3886
4.21k
    TL_ASSERT(has_value());
3887
4.21k
    return val();
3888
4.21k
  }
_ZNR2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEdeIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3885
47.7k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3886
47.7k
    TL_ASSERT(has_value());
3887
47.7k
    return val();
3888
47.7k
  }
_ZNR2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3885
6.77k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3886
6.77k
    TL_ASSERT(has_value());
3887
6.77k
    return val();
3888
6.77k
  }
_ZNR2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Line
Count
Source
3885
130k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3886
130k
    TL_ASSERT(has_value());
3887
130k
    return val();
3888
130k
  }
_ZNR2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEdeIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3885
37.9k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3886
37.9k
    TL_ASSERT(has_value());
3887
37.9k
    return val();
3888
37.9k
  }
3889
  template <class U = T,
3890
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3891
  constexpr const U &&operator*() const && {
3892
    TL_ASSERT(has_value());
3893
    return std::move(val());
3894
  }
3895
  template <class U = T,
3896
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3897
  TL_EXPECTED_11_CONSTEXPR U &&operator*() && {
3898
    TL_ASSERT(has_value());
3899
    return std::move(val());
3900
  }
3901
3902
1.23M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url, ada::errors>::has_value() const
Line
Count
Source
3902
212k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url_aggregator, ada::errors>::has_value() const
Line
Count
Source
3902
521k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::has_value() const
Line
Count
Source
3902
73.0k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::has_value() const
tl::expected<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors>::has_value() const
Line
Count
Source
3902
4.21k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::has_value() const
Line
Count
Source
3902
48.0k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_init, ada::errors>::has_value() const
Line
Count
Source
3902
111k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::has_value() const
Line
Count
Source
3902
220k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::has_value() const
Line
Count
Source
3902
39.5k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
3903
516k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url, ada::errors>::operator bool() const
Line
Count
Source
3903
21.5k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url_aggregator, ada::errors>::operator bool() const
Line
Count
Source
3903
269k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::operator bool() const
Line
Count
Source
3903
73.0k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator bool() const
tl::expected<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors>::operator bool() const
Line
Count
Source
3903
11.2k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::operator bool() const
Line
Count
Source
3903
48.0k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_init, ada::errors>::operator bool() const
Line
Count
Source
3903
14.3k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::operator bool() const
Line
Count
Source
3903
39.5k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::operator bool() const
Line
Count
Source
3903
39.5k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
3904
3905
  template <class U = T,
3906
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3907
  TL_EXPECTED_11_CONSTEXPR const U &value() const & {
3908
    if (!has_value())
3909
      detail::throw_exception(bad_expected_access<E>(err().value()));
3910
    return val();
3911
  }
3912
  template <class U = T,
3913
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3914
0
  TL_EXPECTED_11_CONSTEXPR U &value() & {
3915
0
    if (!has_value())
3916
0
      detail::throw_exception(bad_expected_access<E>(err().value()));
3917
0
    return val();
3918
0
  }
3919
  template <class U = T,
3920
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3921
  TL_EXPECTED_11_CONSTEXPR const U &&value() const && {
3922
    if (!has_value())
3923
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
3924
    return std::move(val());
3925
  }
3926
  template <class U = T,
3927
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3928
  TL_EXPECTED_11_CONSTEXPR U &&value() && {
3929
    if (!has_value())
3930
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
3931
    return std::move(val());
3932
  }
3933
3934
  constexpr const E &error() const & {
3935
    TL_ASSERT(!has_value());
3936
    return err().value();
3937
  }
3938
8.30k
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3939
8.30k
    TL_ASSERT(!has_value());
3940
8.30k
    return err().value();
3941
8.30k
  }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::error() &
Line
Count
Source
3938
1.37k
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3939
1.37k
    TL_ASSERT(!has_value());
3940
1.37k
    return err().value();
3941
1.37k
  }
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::error() &
Line
Count
Source
3938
252
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3939
252
    TL_ASSERT(!has_value());
3940
252
    return err().value();
3941
252
  }
tl::expected<ada::url_pattern_init, ada::errors>::error() &
Line
Count
Source
3938
3.36k
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3939
3.36k
    TL_ASSERT(!has_value());
3940
3.36k
    return err().value();
3941
3.36k
  }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::error() &
Line
Count
Source
3938
1.65k
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3939
1.65k
    TL_ASSERT(!has_value());
3940
1.65k
    return err().value();
3941
1.65k
  }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::error() &
Line
Count
Source
3938
1.65k
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3939
1.65k
    TL_ASSERT(!has_value());
3940
1.65k
    return err().value();
3941
1.65k
  }
3942
  constexpr const E &&error() const && {
3943
    TL_ASSERT(!has_value());
3944
    return std::move(err().value());
3945
  }
3946
  TL_EXPECTED_11_CONSTEXPR E &&error() && {
3947
    TL_ASSERT(!has_value());
3948
    return std::move(err().value());
3949
  }
3950
3951
  template <class U>
3952
  constexpr T value_or(U &&v) const & {
3953
    static_assert(std::is_copy_constructible<T>::value &&
3954
                      std::is_convertible<U &&, T>::value,
3955
                  "T must be copy-constructible and convertible to from U&&");
3956
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
3957
  }
3958
  template <class U>
3959
  TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && {
3960
    static_assert(std::is_move_constructible<T>::value &&
3961
                      std::is_convertible<U &&, T>::value,
3962
                  "T must be move-constructible and convertible to from U&&");
3963
    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
3964
  }
3965
};
3966
3967
namespace detail {
3968
template <class Exp>
3969
using exp_t = typename detail::decay_t<Exp>::value_type;
3970
template <class Exp>
3971
using err_t = typename detail::decay_t<Exp>::error_type;
3972
template <class Exp, class Ret>
3973
using ret_t = expected<Ret, err_t<Exp>>;
3974
3975
#ifdef TL_EXPECTED_CXX14
3976
template <class Exp, class F,
3977
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
3978
          class Ret = decltype(detail::invoke(std::declval<F>(),
3979
                                              *std::declval<Exp>()))>
3980
constexpr auto and_then_impl(Exp &&exp, F &&f) {
3981
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3982
3983
  return exp.has_value()
3984
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
3985
             : Ret(unexpect, std::forward<Exp>(exp).error());
3986
}
3987
3988
template <class Exp, class F,
3989
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
3990
          class Ret = decltype(detail::invoke(std::declval<F>()))>
3991
constexpr auto and_then_impl(Exp &&exp, F &&f) {
3992
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3993
3994
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
3995
                         : Ret(unexpect, std::forward<Exp>(exp).error());
3996
}
3997
#else
3998
template <class>
3999
struct TC;
4000
template <class Exp, class F,
4001
          class Ret = decltype(detail::invoke(std::declval<F>(),
4002
                                              *std::declval<Exp>())),
4003
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr>
4004
auto and_then_impl(Exp &&exp, F &&f) -> Ret {
4005
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4006
4007
  return exp.has_value()
4008
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4009
             : Ret(unexpect, std::forward<Exp>(exp).error());
4010
}
4011
4012
template <class Exp, class F,
4013
          class Ret = decltype(detail::invoke(std::declval<F>())),
4014
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr>
4015
constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret {
4016
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4017
4018
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4019
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4020
}
4021
#endif
4022
4023
#ifdef TL_EXPECTED_CXX14
4024
template <class Exp, class F,
4025
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4026
          class Ret = decltype(detail::invoke(std::declval<F>(),
4027
                                              *std::declval<Exp>())),
4028
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4029
constexpr auto expected_map_impl(Exp &&exp, F &&f) {
4030
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4031
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4032
                                                 *std::forward<Exp>(exp)))
4033
                         : result(unexpect, std::forward<Exp>(exp).error());
4034
}
4035
4036
template <class Exp, class F,
4037
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4038
          class Ret = decltype(detail::invoke(std::declval<F>(),
4039
                                              *std::declval<Exp>())),
4040
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4041
auto expected_map_impl(Exp &&exp, F &&f) {
4042
  using result = expected<void, err_t<Exp>>;
4043
  if (exp.has_value()) {
4044
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4045
    return result();
4046
  }
4047
4048
  return result(unexpect, std::forward<Exp>(exp).error());
4049
}
4050
4051
template <class Exp, class F,
4052
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4053
          class Ret = decltype(detail::invoke(std::declval<F>())),
4054
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4055
constexpr auto expected_map_impl(Exp &&exp, F &&f) {
4056
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4057
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4058
                         : result(unexpect, std::forward<Exp>(exp).error());
4059
}
4060
4061
template <class Exp, class F,
4062
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4063
          class Ret = decltype(detail::invoke(std::declval<F>())),
4064
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4065
auto expected_map_impl(Exp &&exp, F &&f) {
4066
  using result = expected<void, err_t<Exp>>;
4067
  if (exp.has_value()) {
4068
    detail::invoke(std::forward<F>(f));
4069
    return result();
4070
  }
4071
4072
  return result(unexpect, std::forward<Exp>(exp).error());
4073
}
4074
#else
4075
template <class Exp, class F,
4076
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4077
          class Ret = decltype(detail::invoke(std::declval<F>(),
4078
                                              *std::declval<Exp>())),
4079
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4080
4081
constexpr auto expected_map_impl(Exp &&exp, F &&f)
4082
    -> ret_t<Exp, detail::decay_t<Ret>> {
4083
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4084
4085
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4086
                                                 *std::forward<Exp>(exp)))
4087
                         : result(unexpect, std::forward<Exp>(exp).error());
4088
}
4089
4090
template <class Exp, class F,
4091
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4092
          class Ret = decltype(detail::invoke(std::declval<F>(),
4093
                                              *std::declval<Exp>())),
4094
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4095
4096
auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
4097
  if (exp.has_value()) {
4098
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4099
    return {};
4100
  }
4101
4102
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4103
}
4104
4105
template <class Exp, class F,
4106
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4107
          class Ret = decltype(detail::invoke(std::declval<F>())),
4108
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4109
4110
constexpr auto expected_map_impl(Exp &&exp, F &&f)
4111
    -> ret_t<Exp, detail::decay_t<Ret>> {
4112
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4113
4114
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4115
                         : result(unexpect, std::forward<Exp>(exp).error());
4116
}
4117
4118
template <class Exp, class F,
4119
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4120
          class Ret = decltype(detail::invoke(std::declval<F>())),
4121
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4122
4123
auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
4124
  if (exp.has_value()) {
4125
    detail::invoke(std::forward<F>(f));
4126
    return {};
4127
  }
4128
4129
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4130
}
4131
#endif
4132
4133
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4134
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4135
template <class Exp, class F,
4136
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4137
          class Ret = decltype(detail::invoke(std::declval<F>(),
4138
                                              std::declval<Exp>().error())),
4139
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4140
constexpr auto map_error_impl(Exp &&exp, F &&f) {
4141
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4142
  return exp.has_value()
4143
             ? result(*std::forward<Exp>(exp))
4144
             : result(unexpect, detail::invoke(std::forward<F>(f),
4145
                                               std::forward<Exp>(exp).error()));
4146
}
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>().error())),
4151
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4152
auto map_error_impl(Exp &&exp, F &&f) {
4153
  using result = expected<exp_t<Exp>, monostate>;
4154
  if (exp.has_value()) {
4155
    return result(*std::forward<Exp>(exp));
4156
  }
4157
4158
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4159
  return result(unexpect, monostate{});
4160
}
4161
template <class Exp, class F,
4162
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4163
          class Ret = decltype(detail::invoke(std::declval<F>(),
4164
                                              std::declval<Exp>().error())),
4165
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4166
constexpr auto map_error_impl(Exp &&exp, F &&f) {
4167
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4168
  return exp.has_value()
4169
             ? result()
4170
             : result(unexpect, detail::invoke(std::forward<F>(f),
4171
                                               std::forward<Exp>(exp).error()));
4172
}
4173
template <class Exp, class F,
4174
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4175
          class Ret = decltype(detail::invoke(std::declval<F>(),
4176
                                              std::declval<Exp>().error())),
4177
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4178
auto map_error_impl(Exp &&exp, F &&f) {
4179
  using result = expected<exp_t<Exp>, monostate>;
4180
  if (exp.has_value()) {
4181
    return result();
4182
  }
4183
4184
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4185
  return result(unexpect, monostate{});
4186
}
4187
#else
4188
template <class Exp, class F,
4189
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4190
          class Ret = decltype(detail::invoke(std::declval<F>(),
4191
                                              std::declval<Exp>().error())),
4192
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4193
constexpr auto map_error_impl(Exp &&exp, F &&f)
4194
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4195
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4196
4197
  return exp.has_value()
4198
             ? result(*std::forward<Exp>(exp))
4199
             : result(unexpect, detail::invoke(std::forward<F>(f),
4200
                                               std::forward<Exp>(exp).error()));
4201
}
4202
4203
template <class Exp, class F,
4204
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4205
          class Ret = decltype(detail::invoke(std::declval<F>(),
4206
                                              std::declval<Exp>().error())),
4207
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4208
auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
4209
  using result = expected<exp_t<Exp>, monostate>;
4210
  if (exp.has_value()) {
4211
    return result(*std::forward<Exp>(exp));
4212
  }
4213
4214
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4215
  return result(unexpect, monostate{});
4216
}
4217
4218
template <class Exp, class F,
4219
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4220
          class Ret = decltype(detail::invoke(std::declval<F>(),
4221
                                              std::declval<Exp>().error())),
4222
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4223
constexpr auto map_error_impl(Exp &&exp, F &&f)
4224
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4225
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4226
4227
  return exp.has_value()
4228
             ? result()
4229
             : result(unexpect, detail::invoke(std::forward<F>(f),
4230
                                               std::forward<Exp>(exp).error()));
4231
}
4232
4233
template <class Exp, class F,
4234
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4235
          class Ret = decltype(detail::invoke(std::declval<F>(),
4236
                                              std::declval<Exp>().error())),
4237
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4238
auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
4239
  using result = expected<exp_t<Exp>, monostate>;
4240
  if (exp.has_value()) {
4241
    return result();
4242
  }
4243
4244
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4245
  return result(unexpect, monostate{});
4246
}
4247
#endif
4248
4249
#ifdef TL_EXPECTED_CXX14
4250
template <class Exp, class F,
4251
          class Ret = decltype(detail::invoke(std::declval<F>(),
4252
                                              std::declval<Exp>().error())),
4253
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4254
constexpr auto or_else_impl(Exp &&exp, F &&f) {
4255
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4256
  return exp.has_value() ? std::forward<Exp>(exp)
4257
                         : detail::invoke(std::forward<F>(f),
4258
                                          std::forward<Exp>(exp).error());
4259
}
4260
4261
template <class Exp, class F,
4262
          class Ret = decltype(detail::invoke(std::declval<F>(),
4263
                                              std::declval<Exp>().error())),
4264
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4265
detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
4266
  return exp.has_value() ? std::forward<Exp>(exp)
4267
                         : (detail::invoke(std::forward<F>(f),
4268
                                           std::forward<Exp>(exp).error()),
4269
                            std::forward<Exp>(exp));
4270
}
4271
#else
4272
template <class Exp, class F,
4273
          class Ret = decltype(detail::invoke(std::declval<F>(),
4274
                                              std::declval<Exp>().error())),
4275
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4276
auto or_else_impl(Exp &&exp, F &&f) -> Ret {
4277
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4278
  return exp.has_value() ? std::forward<Exp>(exp)
4279
                         : detail::invoke(std::forward<F>(f),
4280
                                          std::forward<Exp>(exp).error());
4281
}
4282
4283
template <class Exp, class F,
4284
          class Ret = decltype(detail::invoke(std::declval<F>(),
4285
                                              std::declval<Exp>().error())),
4286
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4287
detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
4288
  return exp.has_value() ? std::forward<Exp>(exp)
4289
                         : (detail::invoke(std::forward<F>(f),
4290
                                           std::forward<Exp>(exp).error()),
4291
                            std::forward<Exp>(exp));
4292
}
4293
#endif
4294
}  // namespace detail
4295
4296
template <class T, class E, class U, class F>
4297
constexpr bool operator==(const expected<T, E> &lhs,
4298
                          const expected<U, F> &rhs) {
4299
  return (lhs.has_value() != rhs.has_value())
4300
             ? false
4301
             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
4302
}
4303
template <class T, class E, class U, class F>
4304
constexpr bool operator!=(const expected<T, E> &lhs,
4305
                          const expected<U, F> &rhs) {
4306
  return (lhs.has_value() != rhs.has_value())
4307
             ? true
4308
             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4309
}
4310
template <class E, class F>
4311
constexpr bool operator==(const expected<void, E> &lhs,
4312
                          const expected<void, F> &rhs) {
4313
  return (lhs.has_value() != rhs.has_value())
4314
             ? false
4315
             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
4316
}
4317
template <class E, class F>
4318
constexpr bool operator!=(const expected<void, E> &lhs,
4319
                          const expected<void, F> &rhs) {
4320
  return (lhs.has_value() != rhs.has_value())
4321
             ? true
4322
             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4323
}
4324
4325
template <class T, class E, class U>
4326
constexpr bool operator==(const expected<T, E> &x, const U &v) {
4327
  return x.has_value() ? *x == v : false;
4328
}
4329
template <class T, class E, class U>
4330
constexpr bool operator==(const U &v, const expected<T, E> &x) {
4331
  return x.has_value() ? *x == v : false;
4332
}
4333
template <class T, class E, class U>
4334
constexpr bool operator!=(const expected<T, E> &x, const U &v) {
4335
  return x.has_value() ? *x != v : true;
4336
}
4337
template <class T, class E, class U>
4338
constexpr bool operator!=(const U &v, const expected<T, E> &x) {
4339
  return x.has_value() ? *x != v : true;
4340
}
4341
4342
template <class T, class E>
4343
constexpr bool operator==(const expected<T, E> &x, const unexpected<E> &e) {
4344
  return x.has_value() ? false : x.error() == e.value();
4345
}
4346
template <class T, class E>
4347
constexpr bool operator==(const unexpected<E> &e, const expected<T, E> &x) {
4348
  return x.has_value() ? false : x.error() == e.value();
4349
}
4350
template <class T, class E>
4351
constexpr bool operator!=(const expected<T, E> &x, const unexpected<E> &e) {
4352
  return x.has_value() ? true : x.error() != e.value();
4353
}
4354
template <class T, class E>
4355
constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) {
4356
  return x.has_value() ? true : x.error() != e.value();
4357
}
4358
4359
template <class T, class E,
4360
          detail::enable_if_t<(std::is_void<T>::value ||
4361
                               std::is_move_constructible<T>::value) &&
4362
                              detail::is_swappable<T>::value &&
4363
                              std::is_move_constructible<E>::value &&
4364
                              detail::is_swappable<E>::value> * = nullptr>
4365
void swap(expected<T, E> &lhs,
4366
          expected<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
4367
  lhs.swap(rhs);
4368
}
4369
}  // namespace tl
4370
4371
#endif
4372
/* end file include/ada/expected.h */
4373
4374
/* begin file include/ada/url_pattern_regex.h */
4375
/**
4376
 * @file url_search_params.h
4377
 * @brief Declaration for the URL Search Params
4378
 */
4379
#ifndef ADA_URL_PATTERN_REGEX_H
4380
#define ADA_URL_PATTERN_REGEX_H
4381
4382
#include <string>
4383
#include <string_view>
4384
4385
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4386
#include <regex>
4387
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4388
4389
#if ADA_INCLUDE_URL_PATTERN
4390
namespace ada::url_pattern_regex {
4391
4392
template <typename T>
4393
concept regex_concept = requires(T t, std::string_view pattern,
4394
                                 bool ignore_case, std::string_view input) {
4395
  // Ensure the class has a type alias 'regex_type'
4396
  typename T::regex_type;
4397
4398
  // Function to create a regex instance
4399
  {
4400
    T::create_instance(pattern, ignore_case)
4401
  } -> std::same_as<std::optional<typename T::regex_type>>;
4402
4403
  // Function to perform regex search
4404
  {
4405
    T::regex_search(input, std::declval<typename T::regex_type&>())
4406
  } -> std::same_as<std::optional<std::vector<std::optional<std::string>>>>;
4407
4408
  // Function to match regex pattern
4409
  {
4410
    T::regex_match(input, std::declval<typename T::regex_type&>())
4411
  } -> std::same_as<bool>;
4412
4413
  // Copy constructor
4414
  { T(std::declval<const T&>()) } -> std::same_as<T>;
4415
4416
  // Move constructor
4417
  { T(std::declval<T&&>()) } -> std::same_as<T>;
4418
};
4419
4420
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4421
class std_regex_provider final {
4422
 public:
4423
  std_regex_provider() = default;
4424
  using regex_type = std::regex;
4425
  static std::optional<regex_type> create_instance(std::string_view pattern,
4426
                                                   bool ignore_case);
4427
  static std::optional<std::vector<std::optional<std::string>>> regex_search(
4428
      std::string_view input, const regex_type& pattern);
4429
  static bool regex_match(std::string_view input, const regex_type& pattern);
4430
};
4431
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4432
4433
}  // namespace ada::url_pattern_regex
4434
#endif  // ADA_INCLUDE_URL_PATTERN
4435
#endif  // ADA_URL_PATTERN_REGEX_H
4436
/* end file include/ada/url_pattern_regex.h */
4437
/* begin file include/ada/url_pattern_init.h */
4438
/**
4439
 * @file url_pattern_init.h
4440
 * @brief Declaration for the url_pattern_init implementation.
4441
 */
4442
#ifndef ADA_URL_PATTERN_INIT_H
4443
#define ADA_URL_PATTERN_INIT_H
4444
4445
/* begin file include/ada/errors.h */
4446
/**
4447
 * @file errors.h
4448
 * @brief Error type definitions for URL parsing.
4449
 *
4450
 * Defines the error codes that can be returned when URL parsing fails.
4451
 */
4452
#ifndef ADA_ERRORS_H
4453
#define ADA_ERRORS_H
4454
4455
#include <cstdint>
4456
namespace ada {
4457
/**
4458
 * @brief Error codes for URL parsing operations.
4459
 *
4460
 * Used with `tl::expected` to indicate why a URL parsing operation failed.
4461
 */
4462
enum class errors : uint8_t {
4463
  type_error /**< A type error occurred (e.g., invalid URL syntax). */
4464
};
4465
}  // namespace ada
4466
#endif  // ADA_ERRORS_H
4467
/* end file include/ada/errors.h */
4468
4469
#include <string_view>
4470
#include <string>
4471
#include <optional>
4472
#include <iostream>
4473
4474
#if ADA_TESTING
4475
#include <iostream>
4476
#endif  // ADA_TESTING
4477
4478
#if ADA_INCLUDE_URL_PATTERN
4479
namespace ada {
4480
4481
// Important: C++20 allows us to use concept rather than `using` or `typedef
4482
// and allows functions with second argument, which is optional (using either
4483
// std::nullopt or a parameter with default value)
4484
template <typename F>
4485
concept url_pattern_encoding_callback = requires(F f, std::string_view sv) {
4486
  { f(sv) } -> std::same_as<tl::expected<std::string, errors>>;
4487
};
4488
4489
// A structure providing matching patterns for individual components
4490
// of a URL. When a URLPattern is created, or when a URLPattern is
4491
// used to match or test against a URL, the input can be given as
4492
// either a string or a URLPatternInit struct. If a string is given,
4493
// it will be parsed to create a URLPatternInit. The URLPatternInit
4494
// API is defined as part of the URLPattern specification.
4495
// All provided strings must be valid UTF-8.
4496
struct url_pattern_init {
4497
  enum class process_type : uint8_t {
4498
    url,
4499
    pattern,
4500
  };
4501
4502
0
  friend std::ostream& operator<<(std::ostream& os, process_type type) {
4503
0
    switch (type) {
4504
0
      case process_type::url:
4505
0
        return os << "url";
4506
0
      case process_type::pattern:
4507
0
        return os << "pattern";
4508
0
      default:
4509
0
        return os << "unknown";
4510
0
    }
4511
0
  }
4512
4513
  // All strings must be valid UTF-8.
4514
  // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
4515
  static tl::expected<url_pattern_init, errors> process(
4516
      const url_pattern_init& init, process_type type,
4517
      std::optional<std::string_view> protocol = std::nullopt,
4518
      std::optional<std::string_view> username = std::nullopt,
4519
      std::optional<std::string_view> password = std::nullopt,
4520
      std::optional<std::string_view> hostname = std::nullopt,
4521
      std::optional<std::string_view> port = std::nullopt,
4522
      std::optional<std::string_view> pathname = std::nullopt,
4523
      std::optional<std::string_view> search = std::nullopt,
4524
      std::optional<std::string_view> hash = std::nullopt);
4525
4526
  // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init
4527
  static tl::expected<std::string, errors> process_protocol(
4528
      std::string_view value, process_type type);
4529
4530
  // @see https://urlpattern.spec.whatwg.org/#process-username-for-init
4531
  static tl::expected<std::string, errors> process_username(
4532
      std::string_view value, process_type type);
4533
4534
  // @see https://urlpattern.spec.whatwg.org/#process-password-for-init
4535
  static tl::expected<std::string, errors> process_password(
4536
      std::string_view value, process_type type);
4537
4538
  // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init
4539
  static tl::expected<std::string, errors> process_hostname(
4540
      std::string_view value, process_type type);
4541
4542
  // @see https://urlpattern.spec.whatwg.org/#process-port-for-init
4543
  static tl::expected<std::string, errors> process_port(
4544
      std::string_view port, std::string_view protocol, process_type type);
4545
4546
  // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init
4547
  static tl::expected<std::string, errors> process_pathname(
4548
      std::string_view value, std::string_view protocol, process_type type);
4549
4550
  // @see https://urlpattern.spec.whatwg.org/#process-search-for-init
4551
  static tl::expected<std::string, errors> process_search(
4552
      std::string_view value, process_type type);
4553
4554
  // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init
4555
  static tl::expected<std::string, errors> process_hash(std::string_view value,
4556
                                                        process_type type);
4557
4558
#if ADA_TESTING
4559
  friend void PrintTo(const url_pattern_init& init, std::ostream* os) {
4560
    *os << "protocol: '" << init.protocol.value_or("undefined") << "', ";
4561
    *os << "username: '" << init.username.value_or("undefined") << "', ";
4562
    *os << "password: '" << init.password.value_or("undefined") << "', ";
4563
    *os << "hostname: '" << init.hostname.value_or("undefined") << "', ";
4564
    *os << "port: '" << init.port.value_or("undefined") << "', ";
4565
    *os << "pathname: '" << init.pathname.value_or("undefined") << "', ";
4566
    *os << "search: '" << init.search.value_or("undefined") << "', ";
4567
    *os << "hash: '" << init.hash.value_or("undefined") << "', ";
4568
    *os << "base_url: '" << init.base_url.value_or("undefined") << "', ";
4569
  }
4570
#endif  // ADA_TESTING
4571
4572
  bool operator==(const url_pattern_init&) const;
4573
  // If present, must be valid UTF-8.
4574
  std::optional<std::string> protocol{};
4575
  // If present, must be valid UTF-8.
4576
  std::optional<std::string> username{};
4577
  // If present, must be valid UTF-8.
4578
  std::optional<std::string> password{};
4579
  // If present, must be valid UTF-8.
4580
  std::optional<std::string> hostname{};
4581
  // If present, must be valid UTF-8.
4582
  std::optional<std::string> port{};
4583
  // If present, must be valid UTF-8.
4584
  std::optional<std::string> pathname{};
4585
  // If present, must be valid UTF-8.
4586
  std::optional<std::string> search{};
4587
  // If present, must be valid UTF-8.
4588
  std::optional<std::string> hash{};
4589
  // If present, must be valid UTF-8.
4590
  std::optional<std::string> base_url{};
4591
};
4592
}  // namespace ada
4593
#endif  // ADA_INCLUDE_URL_PATTERN
4594
#endif  // ADA_URL_PATTERN_INIT_H
4595
/* end file include/ada/url_pattern_init.h */
4596
4597
/** @private Forward declarations */
4598
namespace ada {
4599
struct url_aggregator;
4600
struct url;
4601
#if ADA_INCLUDE_URL_PATTERN
4602
template <url_pattern_regex::regex_concept regex_provider>
4603
class url_pattern;
4604
struct url_pattern_options;
4605
#endif  // ADA_INCLUDE_URL_PATTERN
4606
enum class errors : uint8_t;
4607
}  // namespace ada
4608
4609
/**
4610
 * @namespace ada::parser
4611
 * @brief Internal URL parsing implementation.
4612
 *
4613
 * Contains the core URL parsing algorithm as specified by the WHATWG URL
4614
 * Standard. These functions are used internally by `ada::parse()`.
4615
 */
4616
namespace ada::parser {
4617
/**
4618
 * Parses a URL string into a URL object.
4619
 *
4620
 * @tparam result_type The type of URL object to create (url or url_aggregator).
4621
 *
4622
 * @param user_input The URL string to parse (must be valid UTF-8).
4623
 * @param base_url Optional base URL for resolving relative URLs.
4624
 *
4625
 * @return The parsed URL object. Check `is_valid` to determine if parsing
4626
 *         succeeded.
4627
 *
4628
 * @see https://url.spec.whatwg.org/#concept-basic-url-parser
4629
 */
4630
template <typename result_type = url_aggregator>
4631
result_type parse_url(std::string_view user_input,
4632
                      const result_type* base_url = nullptr);
4633
4634
extern template url_aggregator parse_url<url_aggregator>(
4635
    std::string_view user_input, const url_aggregator* base_url);
4636
extern template url parse_url<url>(std::string_view user_input,
4637
                                   const url* base_url);
4638
4639
template <typename result_type = url_aggregator, bool store_values = true>
4640
result_type parse_url_impl(std::string_view user_input,
4641
                           const result_type* base_url = nullptr);
4642
4643
extern template url_aggregator parse_url_impl<url_aggregator>(
4644
    std::string_view user_input, const url_aggregator* base_url);
4645
extern template url parse_url_impl<url>(std::string_view user_input,
4646
                                        const url* base_url);
4647
4648
#if ADA_INCLUDE_URL_PATTERN
4649
template <url_pattern_regex::regex_concept regex_provider>
4650
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
4651
    std::variant<std::string_view, url_pattern_init>&& input,
4652
    const std::string_view* base_url, const url_pattern_options* options);
4653
#endif  // ADA_INCLUDE_URL_PATTERN
4654
4655
}  // namespace ada::parser
4656
4657
#endif  // ADA_PARSER_H
4658
/* end file include/ada/parser.h */
4659
/* begin file include/ada/parser-inl.h */
4660
/**
4661
 * @file parser-inl.h
4662
 */
4663
#ifndef ADA_PARSER_INL_H
4664
#define ADA_PARSER_INL_H
4665
4666
/* begin file include/ada/url_pattern.h */
4667
/**
4668
 * @file url_pattern.h
4669
 * @brief URLPattern API implementation.
4670
 *
4671
 * This header provides the URLPattern API as specified by the WHATWG URL
4672
 * Pattern Standard. URLPattern allows matching URLs against patterns with
4673
 * wildcards and named groups, similar to how regular expressions match strings.
4674
 *
4675
 * @see https://urlpattern.spec.whatwg.org/
4676
 * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
4677
 */
4678
#ifndef ADA_URL_PATTERN_H
4679
#define ADA_URL_PATTERN_H
4680
4681
/* begin file include/ada/implementation.h */
4682
/**
4683
 * @file implementation.h
4684
 * @brief User-facing functions for URL parsing and manipulation.
4685
 *
4686
 * This header provides the primary public API for parsing URLs in Ada.
4687
 * It includes the main `ada::parse()` function which is the recommended
4688
 * entry point for most users.
4689
 *
4690
 * @see https://url.spec.whatwg.org/#api
4691
 */
4692
#ifndef ADA_IMPLEMENTATION_H
4693
#define ADA_IMPLEMENTATION_H
4694
4695
#include <string>
4696
#include <string_view>
4697
#include <optional>
4698
4699
/* begin file include/ada/url.h */
4700
/**
4701
 * @file url.h
4702
 * @brief Declaration for the `ada::url` class.
4703
 *
4704
 * This file contains the `ada::url` struct which represents a parsed URL
4705
 * using separate `std::string` instances for each component. This
4706
 * representation is more flexible but uses more memory than `url_aggregator`.
4707
 *
4708
 * @see url_aggregator.h for a more memory-efficient alternative
4709
 */
4710
#ifndef ADA_URL_H
4711
#define ADA_URL_H
4712
4713
#include <algorithm>
4714
#include <optional>
4715
#include <ostream>
4716
#include <string>
4717
#include <string_view>
4718
4719
/* begin file include/ada/url_components.h */
4720
/**
4721
 * @file url_components.h
4722
 * @brief URL component offset representation for url_aggregator.
4723
 *
4724
 * This file defines the `url_components` struct which stores byte offsets
4725
 * into a URL string buffer. It is used internally by `url_aggregator` to
4726
 * efficiently locate URL components without storing separate strings.
4727
 */
4728
#ifndef ADA_URL_COMPONENTS_H
4729
#define ADA_URL_COMPONENTS_H
4730
4731
namespace ada {
4732
4733
/**
4734
 * @brief Stores byte offsets for URL components within a buffer.
4735
 *
4736
 * The `url_components` struct uses 32-bit offsets to track the boundaries
4737
 * of each URL component within a single string buffer. This enables efficient
4738
 * component extraction without additional memory allocations.
4739
 *
4740
 * Component layout in a URL:
4741
 * ```
4742
 * https://user:pass@example.com:1234/foo/bar?baz#quux
4743
 *       |     |    |          | ^^^^|       |   |
4744
 *       |     |    |          | |   |       |   `----- hash_start
4745
 *       |     |    |          | |   |       `--------- search_start
4746
 *       |     |    |          | |   `----------------- pathname_start
4747
 *       |     |    |          | `--------------------- port
4748
 *       |     |    |          `----------------------- host_end
4749
 *       |     |    `---------------------------------- host_start
4750
 *       |     `--------------------------------------- username_end
4751
 *       `--------------------------------------------- protocol_end
4752
 * ```
4753
 *
4754
 * @note The 32-bit offsets limit URLs to 4GB in length.
4755
 * @note A value of `omitted` (UINT32_MAX) indicates the component is not
4756
 * present.
4757
 */
4758
struct url_components {
4759
  /** Sentinel value indicating a component is not present. */
4760
  constexpr static uint32_t omitted = uint32_t(-1);
4761
4762
89.9k
  url_components() = default;
4763
  url_components(const url_components &u) = default;
4764
  url_components(url_components &&u) noexcept = default;
4765
  url_components &operator=(url_components &&u) noexcept = default;
4766
  url_components &operator=(const url_components &u) = default;
4767
  ~url_components() = default;
4768
4769
  /** Offset of the end of the protocol/scheme (position of ':'). */
4770
  uint32_t protocol_end{0};
4771
4772
  /**
4773
   * Offset of the end of the username.
4774
   * Initialized to 0 (not `omitted`) to simplify username/password getters.
4775
   */
4776
  uint32_t username_end{0};
4777
4778
  /** Offset of the start of the host. */
4779
  uint32_t host_start{0};
4780
4781
  /** Offset of the end of the host. */
4782
  uint32_t host_end{0};
4783
4784
  /** Port number, or `omitted` if no port is specified. */
4785
  uint32_t port{omitted};
4786
4787
  /** Offset of the start of the pathname. */
4788
  uint32_t pathname_start{0};
4789
4790
  /** Offset of the '?' starting the query, or `omitted` if no query. */
4791
  uint32_t search_start{omitted};
4792
4793
  /** Offset of the '#' starting the fragment, or `omitted` if no fragment. */
4794
  uint32_t hash_start{omitted};
4795
4796
  /**
4797
   * Validates that offsets are in ascending order and consistent.
4798
   * Useful for debugging to detect internal corruption.
4799
   * @return `true` if offsets are consistent, `false` otherwise.
4800
   */
4801
  [[nodiscard]] constexpr bool check_offset_consistency() const noexcept;
4802
4803
  /**
4804
   * Returns a JSON string representation of the offsets for debugging.
4805
   * @return A JSON-formatted string with all offset values.
4806
   */
4807
  [[nodiscard]] std::string to_string() const;
4808
4809
};  // struct url_components
4810
}  // namespace ada
4811
#endif
4812
/* end file include/ada/url_components.h */
4813
4814
namespace ada {
4815
4816
struct url_aggregator;
4817
4818
// namespace parser {
4819
// template <typename result_type>
4820
// result_type parse_url(std::string_view user_input,
4821
//                       const result_type* base_url = nullptr);
4822
// template <typename result_type, bool store_values>
4823
// result_type parse_url_impl(std::string_view user_input,
4824
//                            const result_type* base_url = nullptr);
4825
// }
4826
4827
/**
4828
 * @brief Represents a parsed URL with individual string components.
4829
 *
4830
 * The `url` struct stores each URL component (scheme, username, password,
4831
 * host, port, path, query, fragment) as a separate `std::string`. This
4832
 * provides flexibility but incurs more memory allocations compared to
4833
 * `url_aggregator`.
4834
 *
4835
 * **When to use `ada::url`:**
4836
 * - When you need to frequently modify individual URL components
4837
 * - When you want independent ownership of component strings
4838
 *
4839
 * **When to use `ada::url_aggregator` instead:**
4840
 * - For read-mostly operations on parsed URLs
4841
 * - When memory efficiency is important
4842
 * - When you only need string_view access to components
4843
 *
4844
 * @note This type is returned when parsing with `ada::parse<ada::url>()`.
4845
 *       By default, `ada::parse()` returns `ada::url_aggregator`.
4846
 *
4847
 * @see url_aggregator For a more memory-efficient URL representation
4848
 * @see https://url.spec.whatwg.org/#url-representation
4849
 */
4850
struct url : url_base {
4851
21.5k
  url() = default;
4852
  url(const url &u) = default;
4853
15.4k
  url(url &&u) noexcept = default;
4854
  url &operator=(url &&u) noexcept = default;
4855
3.04k
  url &operator=(const url &u) = default;
4856
37.0k
  ~url() override = default;
4857
4858
  /**
4859
   * @private
4860
   * A URL's username is an ASCII string identifying a username. It is initially
4861
   * the empty string.
4862
   */
4863
  std::string username{};
4864
4865
  /**
4866
   * @private
4867
   * A URL's password is an ASCII string identifying a password. It is initially
4868
   * the empty string.
4869
   */
4870
  std::string password{};
4871
4872
  /**
4873
   * @private
4874
   * A URL's host is null or a host. It is initially null.
4875
   */
4876
  std::optional<std::string> host{};
4877
4878
  /**
4879
   * @private
4880
   * A URL's port is either null or a 16-bit unsigned integer that identifies a
4881
   * networking port. It is initially null.
4882
   */
4883
  std::optional<uint16_t> port{};
4884
4885
  /**
4886
   * @private
4887
   * A URL's path is either an ASCII string or a list of zero or more ASCII
4888
   * strings, usually identifying a location.
4889
   */
4890
  std::string path{};
4891
4892
  /**
4893
   * @private
4894
   * A URL's query is either null or an ASCII string. It is initially null.
4895
   */
4896
  std::optional<std::string> query{};
4897
4898
  /**
4899
   * @private
4900
   * A URL's fragment is either null or an ASCII string that can be used for
4901
   * further processing on the resource the URL's other components identify. It
4902
   * is initially null.
4903
   */
4904
  std::optional<std::string> hash{};
4905
4906
  /**
4907
   * Checks if the URL has an empty hostname (host is set but empty string).
4908
   * @return `true` if host exists but is empty, `false` otherwise.
4909
   */
4910
  [[nodiscard]] inline bool has_empty_hostname() const noexcept;
4911
4912
  /**
4913
   * Checks if the URL has a non-default port explicitly specified.
4914
   * @return `true` if a port is present, `false` otherwise.
4915
   */
4916
  [[nodiscard]] inline bool has_port() const noexcept;
4917
4918
  /**
4919
   * Checks if the URL has a hostname (including empty hostnames).
4920
   * @return `true` if host is present, `false` otherwise.
4921
   */
4922
  [[nodiscard]] inline bool has_hostname() const noexcept;
4923
4924
  /**
4925
   * Validates whether the hostname is a valid domain according to RFC 1034.
4926
   * Checks that the domain and its labels have valid lengths (max 255 octets
4927
   * total, max 63 octets per label).
4928
   * @return `true` if the domain is valid, `false` otherwise.
4929
   */
4930
  [[nodiscard]] bool has_valid_domain() const noexcept override;
4931
4932
  /**
4933
   * Returns a JSON string representation of this URL for debugging.
4934
   * @return A JSON-formatted string with all URL components.
4935
   */
4936
  [[nodiscard]] std::string to_string() const override;
4937
4938
  /**
4939
   * Returns the full serialized URL (the href).
4940
   * @return The complete URL string (allocates a new string).
4941
   * @see https://url.spec.whatwg.org/#dom-url-href
4942
   */
4943
  [[nodiscard]] ada_really_inline std::string get_href() const;
4944
4945
  /**
4946
   * Returns the URL's origin as a string (scheme + host + port for special
4947
   * URLs).
4948
   * @return A newly allocated string containing the serialized origin.
4949
   * @see https://url.spec.whatwg.org/#concept-url-origin
4950
   */
4951
  [[nodiscard]] std::string get_origin() const override;
4952
4953
  /**
4954
   * Returns the URL's scheme followed by a colon (e.g., "https:").
4955
   * @return A newly allocated string with the protocol.
4956
   * @see https://url.spec.whatwg.org/#dom-url-protocol
4957
   */
4958
  [[nodiscard]] std::string get_protocol() const;
4959
4960
  /**
4961
   * Returns the URL's host and port (e.g., "example.com:8080").
4962
   * If no port is set, returns just the host. Returns empty string if no host.
4963
   * @return A newly allocated string with host:port.
4964
   * @see https://url.spec.whatwg.org/#dom-url-host
4965
   */
4966
  [[nodiscard]] std::string get_host() const;
4967
4968
  /**
4969
   * Returns the URL's hostname (without port).
4970
   * Returns empty string if no host is set.
4971
   * @return A newly allocated string with the hostname.
4972
   * @see https://url.spec.whatwg.org/#dom-url-hostname
4973
   */
4974
  [[nodiscard]] std::string get_hostname() const;
4975
4976
  /**
4977
   * Returns the URL's path component.
4978
   * @return A string_view pointing to the path.
4979
   * @see https://url.spec.whatwg.org/#dom-url-pathname
4980
   */
4981
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept;
4982
4983
  /**
4984
   * Returns the byte length of the pathname without creating a string.
4985
   * @return Size of the pathname in bytes.
4986
   * @see https://url.spec.whatwg.org/#dom-url-pathname
4987
   */
4988
  [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
4989
4990
  /**
4991
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
4992
   * Returns empty string if no query is set.
4993
   * @return A newly allocated string with the search/query.
4994
   * @see https://url.spec.whatwg.org/#dom-url-search
4995
   */
4996
  [[nodiscard]] std::string get_search() const;
4997
4998
  /**
4999
   * Returns the URL's username component.
5000
   * @return A constant reference to the username string.
5001
   * @see https://url.spec.whatwg.org/#dom-url-username
5002
   */
5003
  [[nodiscard]] const std::string &get_username() const noexcept;
5004
5005
  /**
5006
   * Sets the URL's username, percent-encoding special characters.
5007
   * @param input The new username value.
5008
   * @return `true` on success, `false` if the URL cannot have credentials.
5009
   * @see https://url.spec.whatwg.org/#dom-url-username
5010
   */
5011
  bool set_username(std::string_view input);
5012
5013
  /**
5014
   * Sets the URL's password, percent-encoding special characters.
5015
   * @param input The new password value.
5016
   * @return `true` on success, `false` if the URL cannot have credentials.
5017
   * @see https://url.spec.whatwg.org/#dom-url-password
5018
   */
5019
  bool set_password(std::string_view input);
5020
5021
  /**
5022
   * Sets the URL's port from a string (e.g., "8080").
5023
   * @param input The port string. Empty string removes the port.
5024
   * @return `true` on success, `false` if the URL cannot have a port.
5025
   * @see https://url.spec.whatwg.org/#dom-url-port
5026
   */
5027
  bool set_port(std::string_view input);
5028
5029
  /**
5030
   * Sets the URL's fragment/hash (the part after '#').
5031
   * @param input The new hash value (with or without leading '#').
5032
   * @see https://url.spec.whatwg.org/#dom-url-hash
5033
   */
5034
  void set_hash(std::string_view input);
5035
5036
  /**
5037
   * Sets the URL's query string (the part after '?').
5038
   * @param input The new query value (with or without leading '?').
5039
   * @see https://url.spec.whatwg.org/#dom-url-search
5040
   */
5041
  void set_search(std::string_view input);
5042
5043
  /**
5044
   * Sets the URL's pathname.
5045
   * @param input The new path value.
5046
   * @return `true` on success, `false` if the URL has an opaque path.
5047
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5048
   */
5049
  bool set_pathname(std::string_view input);
5050
5051
  /**
5052
   * Sets the URL's host (hostname and optionally port).
5053
   * @param input The new host value (e.g., "example.com:8080").
5054
   * @return `true` on success, `false` if parsing fails.
5055
   * @see https://url.spec.whatwg.org/#dom-url-host
5056
   */
5057
  bool set_host(std::string_view input);
5058
5059
  /**
5060
   * Sets the URL's hostname (without port).
5061
   * @param input The new hostname value.
5062
   * @return `true` on success, `false` if parsing fails.
5063
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5064
   */
5065
  bool set_hostname(std::string_view input);
5066
5067
  /**
5068
   * Sets the URL's protocol/scheme.
5069
   * @param input The new protocol (with or without trailing ':').
5070
   * @return `true` on success, `false` if the scheme is invalid.
5071
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5072
   */
5073
  bool set_protocol(std::string_view input);
5074
5075
  /**
5076
   * Replaces the entire URL by parsing a new href string.
5077
   * @param input The new URL string to parse.
5078
   * @return `true` on success, `false` if parsing fails.
5079
   * @see https://url.spec.whatwg.org/#dom-url-href
5080
   */
5081
  bool set_href(std::string_view input);
5082
5083
  /**
5084
   * Returns the URL's password component.
5085
   * @return A constant reference to the password string.
5086
   * @see https://url.spec.whatwg.org/#dom-url-password
5087
   */
5088
  [[nodiscard]] const std::string &get_password() const noexcept;
5089
5090
  /**
5091
   * Returns the URL's port as a string (e.g., "8080").
5092
   * Returns empty string if no port is set.
5093
   * @return A newly allocated string with the port.
5094
   * @see https://url.spec.whatwg.org/#dom-url-port
5095
   */
5096
  [[nodiscard]] std::string get_port() const;
5097
5098
  /**
5099
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
5100
   * Returns empty string if no fragment is set.
5101
   * @return A newly allocated string with the hash.
5102
   * @see https://url.spec.whatwg.org/#dom-url-hash
5103
   */
5104
  [[nodiscard]] std::string get_hash() const;
5105
5106
  /**
5107
   * Checks if the URL has credentials (non-empty username or password).
5108
   * @return `true` if username or password is non-empty, `false` otherwise.
5109
   */
5110
  [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5111
5112
  /**
5113
   * Returns the URL component offsets for efficient serialization.
5114
   *
5115
   * The components represent byte offsets into the serialized URL:
5116
   * ```
5117
   * https://user:pass@example.com:1234/foo/bar?baz#quux
5118
   *       |     |    |          | ^^^^|       |   |
5119
   *       |     |    |          | |   |       |   `----- hash_start
5120
   *       |     |    |          | |   |       `--------- search_start
5121
   *       |     |    |          | |   `----------------- pathname_start
5122
   *       |     |    |          | `--------------------- port
5123
   *       |     |    |          `----------------------- host_end
5124
   *       |     |    `---------------------------------- host_start
5125
   *       |     `--------------------------------------- username_end
5126
   *       `--------------------------------------------- protocol_end
5127
   * ```
5128
   * @return A newly constructed url_components struct.
5129
   * @see https://github.com/servo/rust-url
5130
   */
5131
  [[nodiscard]] ada_really_inline ada::url_components get_components()
5132
      const noexcept;
5133
5134
  /**
5135
   * Checks if the URL has a fragment/hash component.
5136
   * @return `true` if hash is present, `false` otherwise.
5137
   */
5138
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
5139
5140
  /**
5141
   * Checks if the URL has a query/search component.
5142
   * @return `true` if query is present, `false` otherwise.
5143
   */
5144
  [[nodiscard]] constexpr bool has_search() const noexcept override;
5145
5146
 private:
5147
  friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5148
                                                   const ada::url *);
5149
  friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5150
      std::string_view, const ada::url_aggregator *);
5151
  friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5152
      ada::url &url);
5153
5154
  friend ada::url ada::parser::parse_url_impl<ada::url, true>(std::string_view,
5155
                                                              const ada::url *);
5156
  friend ada::url_aggregator ada::parser::parse_url_impl<
5157
      ada::url_aggregator, true>(std::string_view, const ada::url_aggregator *);
5158
5159
  inline void update_unencoded_base_hash(std::string_view input);
5160
  inline void update_base_hostname(std::string_view input);
5161
  inline void update_base_search(std::string_view input,
5162
                                 const uint8_t query_percent_encode_set[]);
5163
  inline void update_base_search(std::optional<std::string> &&input);
5164
  inline void update_base_pathname(std::string_view input);
5165
  inline void update_base_username(std::string_view input);
5166
  inline void update_base_password(std::string_view input);
5167
  inline void update_base_port(std::optional<uint16_t> input);
5168
5169
  /**
5170
   * Sets the host or hostname according to override condition.
5171
   * Return true on success.
5172
   * @see https://url.spec.whatwg.org/#hostname-state
5173
   */
5174
  template <bool override_hostname = false>
5175
  bool set_host_or_hostname(std::string_view input);
5176
5177
  /**
5178
   * Return true on success.
5179
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5180
   */
5181
  [[nodiscard]] bool parse_ipv4(std::string_view input);
5182
5183
  /**
5184
   * Return true on success.
5185
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5186
   */
5187
  [[nodiscard]] bool parse_ipv6(std::string_view input);
5188
5189
  /**
5190
   * Return true on success.
5191
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5192
   */
5193
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
5194
5195
  /**
5196
   * A URL's scheme is an ASCII string that identifies the type of URL and can
5197
   * be used to dispatch a URL for further processing after parsing. It is
5198
   * initially the empty string. We only set non_special_scheme when the scheme
5199
   * is non-special, otherwise we avoid constructing string.
5200
   *
5201
   * Special schemes are stored in ada::scheme::details::is_special_list so we
5202
   * typically do not need to store them in each url instance.
5203
   */
5204
  std::string non_special_scheme{};
5205
5206
  /**
5207
   * A URL cannot have a username/password/port if its host is null or the empty
5208
   * string, or its scheme is "file".
5209
   */
5210
  [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5211
5212
  ada_really_inline size_t parse_port(
5213
      std::string_view view, bool check_trailing_content) noexcept override;
5214
5215
1.69k
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5216
1.69k
    return this->parse_port(view, false);
5217
1.69k
  }
5218
5219
  /**
5220
   * Parse the host from the provided input. We assume that
5221
   * the input does not contain spaces or tabs. Control
5222
   * characters and spaces are not trimmed (they should have
5223
   * been removed if needed).
5224
   * Return true on success.
5225
   * @see https://url.spec.whatwg.org/#host-parsing
5226
   */
5227
  [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5228
5229
  template <bool has_state_override = false>
5230
  [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5231
5232
  constexpr void clear_pathname() override;
5233
  constexpr void clear_search() override;
5234
  constexpr void set_protocol_as_file();
5235
5236
  /**
5237
   * Parse the path from the provided input.
5238
   * Return true on success. Control characters not
5239
   * trimmed from the ends (they should have
5240
   * been removed if needed).
5241
   *
5242
   * The input is expected to be UTF-8.
5243
   *
5244
   * @see https://url.spec.whatwg.org/
5245
   */
5246
  ada_really_inline void parse_path(std::string_view input);
5247
5248
  /**
5249
   * Set the scheme for this URL. The provided scheme should be a valid
5250
   * scheme string, be lower-cased, not contain spaces or tabs. It should
5251
   * have no spurious trailing or leading content.
5252
   */
5253
  inline void set_scheme(std::string &&new_scheme) noexcept;
5254
5255
  /**
5256
   * Take the scheme from another URL. The scheme string is moved from the
5257
   * provided url.
5258
   */
5259
  constexpr void copy_scheme(ada::url &&u);
5260
5261
  /**
5262
   * Take the scheme from another URL. The scheme string is copied from the
5263
   * provided url.
5264
   */
5265
  constexpr void copy_scheme(const ada::url &u);
5266
5267
};  // struct url
5268
5269
inline std::ostream &operator<<(std::ostream &out, const ada::url &u);
5270
}  // namespace ada
5271
5272
#endif  // ADA_URL_H
5273
/* end file include/ada/url.h */
5274
5275
namespace ada {
5276
5277
/**
5278
 * Result type for URL parsing operations.
5279
 *
5280
 * Uses `tl::expected` to represent either a successfully parsed URL or an
5281
 * error. This allows for exception-free error handling.
5282
 *
5283
 * @tparam result_type The URL type to return (default: `ada::url_aggregator`)
5284
 *
5285
 * @example
5286
 * ```cpp
5287
 * ada::result<ada::url_aggregator> result = ada::parse("https://example.com");
5288
 * if (result) {
5289
 *     // Success: use result.value() or *result
5290
 * } else {
5291
 *     // Error: handle result.error()
5292
 * }
5293
 * ```
5294
 */
5295
template <class result_type = ada::url_aggregator>
5296
using result = tl::expected<result_type, ada::errors>;
5297
5298
/**
5299
 * Parses a URL string according to the WHATWG URL Standard.
5300
 *
5301
 * This is the main entry point for URL parsing in Ada. The function takes
5302
 * a string input and optionally a base URL for resolving relative URLs.
5303
 *
5304
 * @tparam result_type The URL type to return. Can be either `ada::url` or
5305
 *         `ada::url_aggregator` (default). The `url_aggregator` type is more
5306
 *         memory-efficient as it stores components as offsets into a single
5307
 *         buffer.
5308
 *
5309
 * @param input The URL string to parse. Must be valid ASCII or UTF-8 encoded.
5310
 *        Leading and trailing whitespace is automatically trimmed.
5311
 * @param base_url Optional pointer to a base URL for resolving relative URLs.
5312
 *        If nullptr (default), only absolute URLs can be parsed successfully.
5313
 *
5314
 * @return A `result<result_type>` containing either the parsed URL on success,
5315
 *         or an error code on failure. Use the boolean conversion or
5316
 *         `has_value()` to check for success.
5317
 *
5318
 * @note The parser is fully compliant with the WHATWG URL Standard.
5319
 *
5320
 * @example
5321
 * ```cpp
5322
 * // Parse an absolute URL
5323
 * auto url = ada::parse("https://user:pass@example.com:8080/path?query#hash");
5324
 * if (url) {
5325
 *     std::cout << url->get_hostname(); // "example.com"
5326
 *     std::cout << url->get_pathname(); // "/path"
5327
 * }
5328
 *
5329
 * // Parse a relative URL with a base
5330
 * auto base = ada::parse("https://example.com/dir/");
5331
 * if (base) {
5332
 *     auto relative = ada::parse("../other/page", &*base);
5333
 *     if (relative) {
5334
 *         std::cout << relative->get_href(); //
5335
 * "https://example.com/other/page"
5336
 *     }
5337
 * }
5338
 * ```
5339
 *
5340
 * @see https://url.spec.whatwg.org/#url-parsing
5341
 */
5342
template <class result_type = ada::url_aggregator>
5343
ada_warn_unused ada::result<result_type> parse(
5344
    std::string_view input, const result_type* base_url = nullptr);
5345
5346
extern template ada::result<url> parse<url>(std::string_view input,
5347
                                            const url* base_url);
5348
extern template ada::result<url_aggregator> parse<url_aggregator>(
5349
    std::string_view input, const url_aggregator* base_url);
5350
5351
/**
5352
 * Checks whether a URL string can be successfully parsed.
5353
 *
5354
 * This is a fast validation function that checks if a URL string is valid
5355
 * according to the WHATWG URL Standard without fully constructing a URL
5356
 * object. Use this when you only need to validate URLs without needing
5357
 * their parsed components.
5358
 *
5359
 * @param input The URL string to validate. Must be valid ASCII or UTF-8.
5360
 * @param base_input Optional pointer to a base URL string for resolving
5361
 *        relative URLs. If nullptr (default), the input is validated as
5362
 *        an absolute URL.
5363
 *
5364
 * @return `true` if the URL can be parsed successfully, `false` otherwise.
5365
 *
5366
 * @example
5367
 * ```cpp
5368
 * // Check absolute URL
5369
 * bool valid = ada::can_parse("https://example.com"); // true
5370
 * bool invalid = ada::can_parse("not a url");         // false
5371
 *
5372
 * // Check relative URL with base
5373
 * std::string_view base = "https://example.com/";
5374
 * bool relative_valid = ada::can_parse("../path", &base); // true
5375
 * ```
5376
 *
5377
 * @see https://url.spec.whatwg.org/#dom-url-canparse
5378
 */
5379
bool can_parse(std::string_view input,
5380
               const std::string_view* base_input = nullptr);
5381
5382
#if ADA_INCLUDE_URL_PATTERN
5383
/**
5384
 * Parses a URL pattern according to the URLPattern specification.
5385
 *
5386
 * URL patterns provide a syntax for matching URLs against patterns, similar
5387
 * to how regular expressions match strings. This is useful for routing and
5388
 * URL-based dispatching.
5389
 *
5390
 * @tparam regex_provider The regex implementation to use for pattern matching.
5391
 *
5392
 * @param input Either a URL pattern string (valid UTF-8) or a URLPatternInit
5393
 *        struct specifying individual component patterns.
5394
 * @param base_url Optional pointer to a base URL string (valid UTF-8) for
5395
 *        resolving relative patterns.
5396
 * @param options Optional pointer to configuration options (e.g., ignore_case).
5397
 *
5398
 * @return A `tl::expected` containing either the parsed url_pattern on success,
5399
 *         or an error code on failure.
5400
 *
5401
 * @see https://urlpattern.spec.whatwg.org
5402
 */
5403
template <url_pattern_regex::regex_concept regex_provider>
5404
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
5405
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
5406
                  const std::string_view* base_url = nullptr,
5407
                  const url_pattern_options* options = nullptr);
5408
#endif  // ADA_INCLUDE_URL_PATTERN
5409
5410
/**
5411
 * Converts a file system path to a file:// URL.
5412
 *
5413
 * Creates a properly formatted file URL from a local file system path.
5414
 * Handles platform-specific path separators and percent-encoding.
5415
 *
5416
 * @param path The file system path to convert. Must be valid ASCII or UTF-8.
5417
 *
5418
 * @return A file:// URL string representing the given path.
5419
 */
5420
std::string href_from_file(std::string_view path);
5421
}  // namespace ada
5422
5423
#endif  // ADA_IMPLEMENTATION_H
5424
/* end file include/ada/implementation.h */
5425
5426
#include <ostream>
5427
#include <string>
5428
#include <string_view>
5429
#include <unordered_map>
5430
#include <variant>
5431
#include <vector>
5432
5433
#if ADA_TESTING
5434
#include <iostream>
5435
#endif  // ADA_TESTING
5436
5437
#if ADA_INCLUDE_URL_PATTERN
5438
namespace ada {
5439
5440
enum class url_pattern_part_type : uint8_t {
5441
  // The part represents a simple fixed text string.
5442
  FIXED_TEXT,
5443
  // The part represents a matching group with a custom regular expression.
5444
  REGEXP,
5445
  // The part represents a matching group that matches code points up to the
5446
  // next separator code point. This is typically used for a named group like
5447
  // ":foo" that does not have a custom regular expression.
5448
  SEGMENT_WILDCARD,
5449
  // The part represents a matching group that greedily matches all code points.
5450
  // This is typically used for the "*" wildcard matching group.
5451
  FULL_WILDCARD,
5452
};
5453
5454
// Pattern type for fast-path matching optimization.
5455
// This allows skipping expensive regex evaluation for common simple patterns.
5456
enum class url_pattern_component_type : uint8_t {
5457
  // Pattern is "^$" - only matches empty string
5458
  EMPTY,
5459
  // Pattern is "^<literal>$" - exact string match (no regex needed)
5460
  EXACT_MATCH,
5461
  // Pattern is "^(.*)$" - matches anything (full wildcard)
5462
  FULL_WILDCARD,
5463
  // Pattern requires actual regex evaluation
5464
  REGEXP,
5465
};
5466
5467
enum class url_pattern_part_modifier : uint8_t {
5468
  // The part does not have a modifier.
5469
  none,
5470
  // The part has an optional modifier indicated by the U+003F (?) code point.
5471
  optional,
5472
  // The part has a "zero or more" modifier indicated by the U+002A (*) code
5473
  // point.
5474
  zero_or_more,
5475
  // The part has a "one or more" modifier indicated by the U+002B (+) code
5476
  // point.
5477
  one_or_more,
5478
};
5479
5480
// @see https://urlpattern.spec.whatwg.org/#part
5481
class url_pattern_part {
5482
 public:
5483
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5484
                   url_pattern_part_modifier _modifier)
5485
12.6k
      : type(_type), value(std::move(_value)), modifier(_modifier) {}
5486
5487
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5488
                   url_pattern_part_modifier _modifier, std::string&& _name,
5489
                   std::string&& _prefix, std::string&& _suffix)
5490
21.0k
      : type(_type),
5491
21.0k
        value(std::move(_value)),
5492
21.0k
        modifier(_modifier),
5493
21.0k
        name(std::move(_name)),
5494
21.0k
        prefix(std::move(_prefix)),
5495
21.0k
        suffix(std::move(_suffix)) {}
5496
  // A part has an associated type, a string, which must be set upon creation.
5497
  url_pattern_part_type type;
5498
  // A part has an associated value, a string, which must be set upon creation.
5499
  std::string value;
5500
  // A part has an associated modifier a string, which must be set upon
5501
  // creation.
5502
  url_pattern_part_modifier modifier;
5503
  // A part has an associated name, a string, initially the empty string.
5504
  std::string name{};
5505
  // A part has an associated prefix, a string, initially the empty string.
5506
  std::string prefix{};
5507
  // A part has an associated suffix, a string, initially the empty string.
5508
  std::string suffix{};
5509
5510
  inline bool is_regexp() const noexcept;
5511
};
5512
5513
// @see https://urlpattern.spec.whatwg.org/#options-header
5514
struct url_pattern_compile_component_options {
5515
  url_pattern_compile_component_options() = default;
5516
  explicit url_pattern_compile_component_options(
5517
      std::optional<char> new_delimiter = std::nullopt,
5518
      std::optional<char> new_prefix = std::nullopt)
5519
36
      : delimiter(new_delimiter), prefix(new_prefix) {}
5520
5521
  inline std::string_view get_delimiter() const ada_warn_unused;
5522
  inline std::string_view get_prefix() const ada_warn_unused;
5523
5524
  // @see https://urlpattern.spec.whatwg.org/#options-ignore-case
5525
  bool ignore_case = false;
5526
5527
  static url_pattern_compile_component_options DEFAULT;
5528
  static url_pattern_compile_component_options HOSTNAME;
5529
  static url_pattern_compile_component_options PATHNAME;
5530
5531
 private:
5532
  // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point
5533
  std::optional<char> delimiter{};
5534
  // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point
5535
  std::optional<char> prefix{};
5536
};
5537
5538
// The default options is an options struct with delimiter code point set to
5539
// the empty string and prefix code point set to the empty string.
5540
inline url_pattern_compile_component_options
5541
    url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt);
5542
5543
// The hostname options is an options struct with delimiter code point set
5544
// "." and prefix code point set to the empty string.
5545
inline url_pattern_compile_component_options
5546
    url_pattern_compile_component_options::HOSTNAME('.', std::nullopt);
5547
5548
// The pathname options is an options struct with delimiter code point set
5549
// "/" and prefix code point set to "/".
5550
inline url_pattern_compile_component_options
5551
    url_pattern_compile_component_options::PATHNAME('/', '/');
5552
5553
// A struct providing the URLPattern matching results for a single
5554
// URL component. The URLPatternComponentResult is only ever used
5555
// as a member attribute of a URLPatternResult struct. The
5556
// URLPatternComponentResult API is defined as part of the URLPattern
5557
// specification.
5558
struct url_pattern_component_result {
5559
  std::string input;
5560
  std::unordered_map<std::string, std::optional<std::string>> groups;
5561
5562
  bool operator==(const url_pattern_component_result&) const;
5563
5564
#if ADA_TESTING
5565
  friend void PrintTo(const url_pattern_component_result& result,
5566
                      std::ostream* os) {
5567
    *os << "input: '" << result.input << "', group: ";
5568
    for (const auto& group : result.groups) {
5569
      *os << "(" << group.first << ", " << group.second.value_or("undefined")
5570
          << ") ";
5571
    }
5572
  }
5573
#endif  // ADA_TESTING
5574
};
5575
5576
template <url_pattern_regex::regex_concept regex_provider>
5577
class url_pattern_component {
5578
 public:
5579
33.7k
  url_pattern_component() = default;
5580
5581
  // This function explicitly takes a std::string because it is moved.
5582
  // To avoid unnecessary copy, move each value while calling the constructor.
5583
  url_pattern_component(std::string&& new_pattern,
5584
                        typename regex_provider::regex_type&& new_regexp,
5585
                        std::vector<std::string>&& new_group_name_list,
5586
                        bool new_has_regexp_groups,
5587
                        url_pattern_component_type new_type,
5588
                        std::string&& new_exact_match_value = {})
5589
37.9k
      : regexp(std::move(new_regexp)),
5590
37.9k
        pattern(std::move(new_pattern)),
5591
37.9k
        group_name_list(std::move(new_group_name_list)),
5592
37.9k
        exact_match_value(std::move(new_exact_match_value)),
5593
37.9k
        has_regexp_groups(new_has_regexp_groups),
5594
37.9k
        type(new_type) {}
5595
5596
  // @see https://urlpattern.spec.whatwg.org/#compile-a-component
5597
  template <url_pattern_encoding_callback F>
5598
  static tl::expected<url_pattern_component, errors> compile(
5599
      std::string_view input, F& encoding_callback,
5600
      url_pattern_compile_component_options& options);
5601
5602
  // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result
5603
  url_pattern_component_result create_component_match_result(
5604
      std::string&& input,
5605
      std::vector<std::optional<std::string>>&& exec_result);
5606
5607
  // Fast path test that returns true/false without constructing result groups.
5608
  // Uses cached pattern type to skip regex evaluation for simple patterns.
5609
  bool fast_test(std::string_view input) const noexcept;
5610
5611
  // Fast path match that returns capture groups without regex for simple
5612
  // patterns. Returns nullopt if pattern doesn't match, otherwise returns
5613
  // capture groups.
5614
  std::optional<std::vector<std::optional<std::string>>> fast_match(
5615
      std::string_view input) const;
5616
5617
#if ADA_TESTING
5618
  friend void PrintTo(const url_pattern_component& component,
5619
                      std::ostream* os) {
5620
    *os << "pattern: '" << component.pattern
5621
        << "', has_regexp_groups: " << component.has_regexp_groups
5622
        << "group_name_list: ";
5623
    for (const auto& name : component.group_name_list) {
5624
      *os << name << ", ";
5625
    }
5626
  }
5627
#endif  // ADA_TESTING
5628
5629
  typename regex_provider::regex_type regexp{};
5630
  std::string pattern{};
5631
  std::vector<std::string> group_name_list{};
5632
  // For EXACT_MATCH type: the literal string to compare against
5633
  std::string exact_match_value{};
5634
  bool has_regexp_groups = false;
5635
  // Cached pattern type for fast-path optimization
5636
  url_pattern_component_type type = url_pattern_component_type::REGEXP;
5637
};
5638
5639
// A URLPattern input can be either a string or a URLPatternInit object.
5640
// If it is a string, it must be a valid UTF-8 string.
5641
using url_pattern_input = std::variant<std::string_view, url_pattern_init>;
5642
5643
// A struct providing the URLPattern matching results for all
5644
// components of a URL. The URLPatternResult API is defined as
5645
// part of the URLPattern specification.
5646
struct url_pattern_result {
5647
  std::vector<url_pattern_input> inputs;
5648
  url_pattern_component_result protocol;
5649
  url_pattern_component_result username;
5650
  url_pattern_component_result password;
5651
  url_pattern_component_result hostname;
5652
  url_pattern_component_result port;
5653
  url_pattern_component_result pathname;
5654
  url_pattern_component_result search;
5655
  url_pattern_component_result hash;
5656
};
5657
5658
struct url_pattern_options {
5659
  bool ignore_case = false;
5660
5661
#if ADA_TESTING
5662
  friend void PrintTo(const url_pattern_options& options, std::ostream* os) {
5663
    *os << "ignore_case: '" << options.ignore_case;
5664
  }
5665
#endif  // ADA_TESTING
5666
};
5667
5668
/**
5669
 * @brief URL pattern matching class implementing the URLPattern API.
5670
 *
5671
 * URLPattern provides a way to match URLs against patterns with wildcards
5672
 * and named capture groups. It's useful for routing, URL-based dispatching,
5673
 * and URL validation.
5674
 *
5675
 * Pattern syntax supports:
5676
 * - Literal text matching
5677
 * - Named groups: `:name` (matches up to the next separator)
5678
 * - Wildcards: `*` (matches everything)
5679
 * - Custom regex: `(pattern)`
5680
 * - Optional segments: `:name?`
5681
 * - Repeated segments: `:name+`, `:name*`
5682
 *
5683
 * @tparam regex_provider The regex implementation to use for pattern matching.
5684
 *         Must satisfy the url_pattern_regex::regex_concept.
5685
 *
5686
 * @note All string inputs must be valid UTF-8.
5687
 *
5688
 * @see https://urlpattern.spec.whatwg.org/
5689
 */
5690
template <url_pattern_regex::regex_concept regex_provider>
5691
class url_pattern {
5692
 public:
5693
4.21k
  url_pattern() = default;
5694
5695
  /**
5696
   * If non-null, base_url must pointer at a valid UTF-8 string.
5697
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec
5698
   */
5699
  result<std::optional<url_pattern_result>> exec(
5700
      const url_pattern_input& input,
5701
      const std::string_view* base_url = nullptr);
5702
5703
  /**
5704
   * If non-null, base_url must pointer at a valid UTF-8 string.
5705
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
5706
   */
5707
  result<bool> test(const url_pattern_input& input,
5708
                    const std::string_view* base_url = nullptr);
5709
5710
  /**
5711
   * @see https://urlpattern.spec.whatwg.org/#url-pattern-match
5712
   * This function expects a valid UTF-8 string if input is a string.
5713
   */
5714
  result<std::optional<url_pattern_result>> match(
5715
      const url_pattern_input& input,
5716
      const std::string_view* base_url_string = nullptr);
5717
5718
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol
5719
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
5720
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username
5721
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
5722
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password
5723
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
5724
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname
5725
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
5726
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port
5727
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
5728
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname
5729
  [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound;
5730
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search
5731
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
5732
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash
5733
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
5734
5735
  // If ignoreCase is true, the JavaScript regular expression created for each
5736
  // pattern must use the `vi` flag. Otherwise, they must use the `v` flag.
5737
  [[nodiscard]] bool ignore_case() const;
5738
5739
  // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups
5740
  [[nodiscard]] bool has_regexp_groups() const;
5741
5742
  // Helper to test all components at once. Returns true if all match.
5743
  [[nodiscard]] bool test_components(
5744
      std::string_view protocol, std::string_view username,
5745
      std::string_view password, std::string_view hostname,
5746
      std::string_view port, std::string_view pathname, std::string_view search,
5747
      std::string_view hash) const;
5748
5749
#if ADA_TESTING
5750
  friend void PrintTo(const url_pattern& c, std::ostream* os) {
5751
    *os << "protocol_component: '" << c.get_protocol() << ", ";
5752
    *os << "username_component: '" << c.get_username() << ", ";
5753
    *os << "password_component: '" << c.get_password() << ", ";
5754
    *os << "hostname_component: '" << c.get_hostname() << ", ";
5755
    *os << "port_component: '" << c.get_port() << ", ";
5756
    *os << "pathname_component: '" << c.get_pathname() << ", ";
5757
    *os << "search_component: '" << c.get_search() << ", ";
5758
    *os << "hash_component: '" << c.get_hash();
5759
  }
5760
#endif  // ADA_TESTING
5761
5762
  template <url_pattern_regex::regex_concept P>
5763
  friend tl::expected<url_pattern<P>, errors> parser::parse_url_pattern_impl(
5764
      std::variant<std::string_view, url_pattern_init>&& input,
5765
      const std::string_view* base_url, const url_pattern_options* options);
5766
5767
  /**
5768
   * @private
5769
   * We can not make this private due to a LLVM bug.
5770
   * Ref: https://github.com/ada-url/ada/pull/859
5771
   */
5772
  url_pattern_component<regex_provider> protocol_component{};
5773
  /**
5774
   * @private
5775
   * We can not make this private due to a LLVM bug.
5776
   * Ref: https://github.com/ada-url/ada/pull/859
5777
   */
5778
  url_pattern_component<regex_provider> username_component{};
5779
  /**
5780
   * @private
5781
   * We can not make this private due to a LLVM bug.
5782
   * Ref: https://github.com/ada-url/ada/pull/859
5783
   */
5784
  url_pattern_component<regex_provider> password_component{};
5785
  /**
5786
   * @private
5787
   * We can not make this private due to a LLVM bug.
5788
   * Ref: https://github.com/ada-url/ada/pull/859
5789
   */
5790
  url_pattern_component<regex_provider> hostname_component{};
5791
  /**
5792
   * @private
5793
   * We can not make this private due to a LLVM bug.
5794
   * Ref: https://github.com/ada-url/ada/pull/859
5795
   */
5796
  url_pattern_component<regex_provider> port_component{};
5797
  /**
5798
   * @private
5799
   * We can not make this private due to a LLVM bug.
5800
   * Ref: https://github.com/ada-url/ada/pull/859
5801
   */
5802
  url_pattern_component<regex_provider> pathname_component{};
5803
  /**
5804
   * @private
5805
   * We can not make this private due to a LLVM bug.
5806
   * Ref: https://github.com/ada-url/ada/pull/859
5807
   */
5808
  url_pattern_component<regex_provider> search_component{};
5809
  /**
5810
   * @private
5811
   * We can not make this private due to a LLVM bug.
5812
   * Ref: https://github.com/ada-url/ada/pull/859
5813
   */
5814
  url_pattern_component<regex_provider> hash_component{};
5815
  /**
5816
   * @private
5817
   * We can not make this private due to a LLVM bug.
5818
   * Ref: https://github.com/ada-url/ada/pull/859
5819
   */
5820
  bool ignore_case_ = false;
5821
};
5822
}  // namespace ada
5823
#endif  // ADA_INCLUDE_URL_PATTERN
5824
#endif
5825
/* end file include/ada/url_pattern.h */
5826
/* begin file include/ada/url_pattern_helpers.h */
5827
/**
5828
 * @file url_pattern_helpers.h
5829
 * @brief Declaration for the URLPattern helpers.
5830
 */
5831
#ifndef ADA_URL_PATTERN_HELPERS_H
5832
#define ADA_URL_PATTERN_HELPERS_H
5833
5834
5835
#include <string>
5836
#include <tuple>
5837
#include <vector>
5838
5839
#if ADA_INCLUDE_URL_PATTERN
5840
namespace ada {
5841
enum class errors : uint8_t;
5842
}
5843
5844
namespace ada::url_pattern_helpers {
5845
5846
// @see https://urlpattern.spec.whatwg.org/#token
5847
enum class token_type : uint8_t {
5848
  INVALID_CHAR,    // 0
5849
  OPEN,            // 1
5850
  CLOSE,           // 2
5851
  REGEXP,          // 3
5852
  NAME,            // 4
5853
  CHAR,            // 5
5854
  ESCAPED_CHAR,    // 6
5855
  OTHER_MODIFIER,  // 7
5856
  ASTERISK,        // 8
5857
  END,             // 9
5858
};
5859
5860
#ifdef ADA_TESTING
5861
std::string to_string(token_type type);
5862
#endif  // ADA_TESTING
5863
5864
// @see https://urlpattern.spec.whatwg.org/#tokenize-policy
5865
enum class token_policy {
5866
  strict,
5867
  lenient,
5868
};
5869
5870
// @see https://urlpattern.spec.whatwg.org/#tokens
5871
class token {
5872
 public:
5873
  token(token_type _type, size_t _index, std::string_view _value)
5874
349k
      : type(_type), index(_index), value(_value) {}
5875
5876
  // A token has an associated type, a string, initially "invalid-char".
5877
  token_type type = token_type::INVALID_CHAR;
5878
5879
  // A token has an associated index, a number, initially 0. It is the position
5880
  // of the first code point in the pattern string represented by the token.
5881
  size_t index = 0;
5882
5883
  // A token has an associated value, a string, initially the empty string. It
5884
  // contains the code points from the pattern string represented by the token.
5885
  std::string_view value{};
5886
};
5887
5888
// @see https://urlpattern.spec.whatwg.org/#pattern-parser
5889
template <url_pattern_encoding_callback F>
5890
class url_pattern_parser {
5891
 public:
5892
  url_pattern_parser(F& encoding_callback_,
5893
                     std::string_view segment_wildcard_regexp_)
5894
39.5k
      : encoding_callback(encoding_callback_),
5895
39.5k
        segment_wildcard_regexp(segment_wildcard_regexp_) {}
5896
5897
198k
  bool can_continue() const { return index < tokens.size(); }
5898
5899
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token
5900
  token* try_consume_token(token_type type);
5901
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token
5902
  token* try_consume_modifier_token();
5903
  // @see
5904
  // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token
5905
  token* try_consume_regexp_or_wildcard_token(const token* name_token);
5906
  // @see https://urlpattern.spec.whatwg.org/#consume-text
5907
  std::string consume_text();
5908
  // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token
5909
  bool consume_required_token(token_type type);
5910
  // @see
5911
  // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value
5912
  std::optional<errors> maybe_add_part_from_the_pending_fixed_value()
5913
      ada_warn_unused;
5914
  // @see https://urlpattern.spec.whatwg.org/#add-a-part
5915
  std::optional<errors> add_part(std::string_view prefix, token* name_token,
5916
                                 token* regexp_or_wildcard_token,
5917
                                 std::string_view suyffix,
5918
                                 token* modifier_token) ada_warn_unused;
5919
5920
  std::vector<token> tokens{};
5921
  F& encoding_callback;
5922
  std::string segment_wildcard_regexp;
5923
  std::vector<url_pattern_part> parts{};
5924
  std::string pending_fixed_value{};
5925
  size_t index = 0;
5926
  size_t next_numeric_name = 0;
5927
};
5928
5929
// @see https://urlpattern.spec.whatwg.org/#tokenizer
5930
class Tokenizer {
5931
 public:
5932
  explicit Tokenizer(std::string_view new_input, token_policy new_policy)
5933
48.0k
      : input(new_input), policy(new_policy) {}
5934
5935
  // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point
5936
  constexpr void get_next_code_point();
5937
5938
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
5939
  constexpr void seek_and_get_next_code_point(size_t index);
5940
5941
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
5942
5943
  void add_token(token_type type, size_t next_position, size_t value_position,
5944
                 size_t value_length);
5945
5946
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
5947
  void add_token_with_default_length(token_type type, size_t next_position,
5948
                                     size_t value_position);
5949
5950
  // @see
5951
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
5952
  void add_token_with_defaults(token_type type);
5953
5954
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
5955
  std::optional<errors> process_tokenizing_error(
5956
      size_t next_position, size_t value_position) ada_warn_unused;
5957
5958
  friend tl::expected<std::vector<token>, errors> tokenize(
5959
      std::string_view input, token_policy policy);
5960
5961
 private:
5962
  // has an associated input, a pattern string, initially the empty string.
5963
  std::string_view input;
5964
  // has an associated policy, a tokenize policy, initially "strict".
5965
  token_policy policy;
5966
  // has an associated token list, a token list, initially an empty list.
5967
  std::vector<token> token_list{};
5968
  // has an associated index, a number, initially 0.
5969
  size_t index = 0;
5970
  // has an associated next index, a number, initially 0.
5971
  size_t next_index = 0;
5972
  // has an associated code point, a Unicode code point, initially null.
5973
  char32_t code_point{};
5974
};
5975
5976
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
5977
template <url_pattern_regex::regex_concept regex_provider>
5978
struct constructor_string_parser {
5979
  explicit constructor_string_parser(std::string_view new_input,
5980
                                     std::vector<token>&& new_token_list)
5981
8.43k
      : input(new_input), token_list(std::move(new_token_list)) {}
5982
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
5983
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
5984
5985
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
5986
  enum class State {
5987
    INIT,
5988
    PROTOCOL,
5989
    AUTHORITY,
5990
    USERNAME,
5991
    PASSWORD,
5992
    HOSTNAME,
5993
    PORT,
5994
    PATHNAME,
5995
    SEARCH,
5996
    HASH,
5997
    DONE,
5998
  };
5999
6000
  // @see
6001
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6002
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6003
6004
 private:
6005
  // @see https://urlpattern.spec.whatwg.org/#rewind
6006
  constexpr void rewind();
6007
6008
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6009
  constexpr bool is_hash_prefix();
6010
6011
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6012
  constexpr bool is_search_prefix();
6013
6014
  // @see https://urlpattern.spec.whatwg.org/#change-state
6015
  void change_state(State state, size_t skip);
6016
6017
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6018
  constexpr bool is_group_open() const;
6019
6020
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6021
  constexpr bool is_group_close() const;
6022
6023
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6024
  constexpr bool is_protocol_suffix() const;
6025
6026
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6027
  constexpr bool next_is_authority_slashes() const;
6028
6029
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6030
  constexpr bool is_an_identity_terminator() const;
6031
6032
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6033
  constexpr bool is_pathname_start() const;
6034
6035
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6036
  constexpr bool is_password_prefix() const;
6037
6038
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6039
  constexpr bool is_an_ipv6_open() const;
6040
6041
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6042
  constexpr bool is_an_ipv6_close() const;
6043
6044
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6045
  constexpr bool is_port_prefix() const;
6046
6047
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6048
  constexpr bool is_non_special_pattern_char(size_t index,
6049
                                             uint32_t value) const;
6050
6051
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6052
  constexpr const token* get_safe_token(size_t index) const;
6053
6054
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6055
  std::string make_component_string();
6056
  // has an associated input, a string, which must be set upon creation.
6057
  std::string_view input;
6058
  // has an associated token list, a token list, which must be set upon
6059
  // creation.
6060
  std::vector<token> token_list;
6061
  // has an associated result, a URLPatternInit, initially set to a new
6062
  // URLPatternInit.
6063
  url_pattern_init result{};
6064
  // has an associated component start, a number, initially set to 0.
6065
  size_t component_start = 0;
6066
  // has an associated token index, a number, initially set to 0.
6067
  size_t token_index = 0;
6068
  // has an associated token increment, a number, initially set to 1.
6069
  size_t token_increment = 1;
6070
  // has an associated group depth, a number, initially set to 0.
6071
  size_t group_depth = 0;
6072
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6073
  // 0.
6074
  size_t hostname_ipv6_bracket_depth = 0;
6075
  // has an associated protocol matches a special scheme flag, a boolean,
6076
  // initially set to false.
6077
  bool protocol_matches_a_special_scheme_flag = false;
6078
  // has an associated state, a string, initially set to "init".
6079
  State state = State::INIT;
6080
};
6081
6082
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6083
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6084
6085
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6086
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6087
6088
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6089
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6090
6091
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6092
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6093
6094
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6095
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6096
    std::string_view input);
6097
6098
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6099
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6100
6101
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6102
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6103
    std::string_view input, std::string_view protocol);
6104
6105
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6106
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6107
6108
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6109
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6110
    std::string_view input);
6111
6112
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6113
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6114
6115
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6116
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6117
6118
// @see https://urlpattern.spec.whatwg.org/#tokenize
6119
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6120
                                                  token_policy policy);
6121
6122
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6123
std::string process_base_url_string(std::string_view input,
6124
                                    url_pattern_init::process_type type);
6125
6126
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6127
std::string escape_pattern_string(std::string_view input);
6128
6129
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6130
std::string escape_regexp_string(std::string_view input);
6131
6132
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6133
constexpr bool is_absolute_pathname(
6134
    std::string_view input, url_pattern_init::process_type type) noexcept;
6135
6136
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6137
template <url_pattern_encoding_callback F>
6138
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6139
    std::string_view input, url_pattern_compile_component_options& options,
6140
    F& encoding_callback);
6141
6142
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6143
std::string generate_pattern_string(
6144
    std::vector<url_pattern_part>& part_list,
6145
    url_pattern_compile_component_options& options);
6146
6147
// @see
6148
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6149
std::tuple<std::string, std::vector<std::string>>
6150
generate_regular_expression_and_name_list(
6151
    const std::vector<url_pattern_part>& part_list,
6152
    url_pattern_compile_component_options options);
6153
6154
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6155
bool is_ipv6_address(std::string_view input) noexcept;
6156
6157
// @see
6158
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6159
template <url_pattern_regex::regex_concept regex_provider>
6160
bool protocol_component_matches_special_scheme(
6161
    ada::url_pattern_component<regex_provider>& input);
6162
6163
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6164
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6165
6166
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6167
std::string generate_segment_wildcard_regexp(
6168
    url_pattern_compile_component_options options);
6169
6170
}  // namespace ada::url_pattern_helpers
6171
#endif  // ADA_INCLUDE_URL_PATTERN
6172
#endif
6173
/* end file include/ada/url_pattern_helpers.h */
6174
6175
#include <string>
6176
#include <string_view>
6177
#include <variant>
6178
6179
namespace ada::parser {
6180
#if ADA_INCLUDE_URL_PATTERN
6181
template <url_pattern_regex::regex_concept regex_provider>
6182
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6183
    std::variant<std::string_view, url_pattern_init>&& input,
6184
11.2k
    const std::string_view* base_url, const url_pattern_options* options) {
6185
  // Let init be null.
6186
11.2k
  url_pattern_init init;
6187
6188
  // If input is a scalar value string then:
6189
11.2k
  if (std::holds_alternative<std::string_view>(input)) {
6190
    // Set init to the result of running parse a constructor string given input.
6191
8.43k
    auto parse_result =
6192
8.43k
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6193
8.43k
            std::get<std::string_view>(input));
6194
8.43k
    if (!parse_result) {
6195
1.65k
      ada_log("constructor_string_parser::parse failed");
6196
1.65k
      return tl::unexpected(parse_result.error());
6197
1.65k
    }
6198
6.77k
    init = std::move(*parse_result);
6199
    // If baseURL is null and init["protocol"] does not exist, then throw a
6200
    // TypeError.
6201
6.77k
    if (!base_url && !init.protocol) {
6202
853
      ada_log("base url is null and protocol is not set");
6203
853
      return tl::unexpected(errors::type_error);
6204
853
    }
6205
6206
    // If baseURL is not null, set init["baseURL"] to baseURL.
6207
5.92k
    if (base_url) {
6208
4.51k
      init.base_url = std::string(*base_url);
6209
4.51k
    }
6210
5.92k
  } else {
6211
    // Assert: input is a URLPatternInit.
6212
2.81k
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6213
    // If baseURL is not null, then throw a TypeError.
6214
2.81k
    if (base_url) {
6215
2.81k
      ada_log("base url is not null");
6216
2.81k
      return tl::unexpected(errors::type_error);
6217
2.81k
    }
6218
    // Optimization: Avoid copy by moving the input value.
6219
    // Set init to input.
6220
0
    init = std::move(std::get<url_pattern_init>(input));
6221
0
  }
6222
6223
  // Let processedInit be the result of process a URLPatternInit given init,
6224
  // "pattern", null, null, null, null, null, null, null, and null.
6225
5.92k
  auto processed_init =
6226
5.92k
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6227
5.92k
  if (!processed_init) {
6228
1.70k
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6229
1.70k
    return tl::unexpected(processed_init.error());
6230
1.70k
  }
6231
6232
  // For each componentName of  "protocol", "username", "password", "hostname",
6233
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6234
  // not exist, then set processedInit[componentName] to "*".
6235
4.21k
  ADA_ASSERT_TRUE(processed_init.has_value());
6236
4.21k
  if (!processed_init->protocol) processed_init->protocol = "*";
6237
4.21k
  if (!processed_init->username) processed_init->username = "*";
6238
4.21k
  if (!processed_init->password) processed_init->password = "*";
6239
4.21k
  if (!processed_init->hostname) processed_init->hostname = "*";
6240
4.21k
  if (!processed_init->port) processed_init->port = "*";
6241
4.21k
  if (!processed_init->pathname) processed_init->pathname = "*";
6242
4.21k
  if (!processed_init->search) processed_init->search = "*";
6243
4.21k
  if (!processed_init->hash) processed_init->hash = "*";
6244
6245
4.21k
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6246
4.21k
  ada_log("-- processed_init->username: ", processed_init->username.value());
6247
4.21k
  ada_log("-- processed_init->password: ", processed_init->password.value());
6248
4.21k
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6249
4.21k
  ada_log("-- processed_init->port: ", processed_init->port.value());
6250
4.21k
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6251
4.21k
  ada_log("-- processed_init->search: ", processed_init->search.value());
6252
4.21k
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6253
6254
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6255
  // is a string which represents its corresponding default port in radix-10
6256
  // using ASCII digits then set processedInit["port"] to the empty string.
6257
  // TODO: Optimization opportunity.
6258
4.21k
  if (scheme::is_special(*processed_init->protocol)) {
6259
4.21k
    std::string_view port = processed_init->port.value();
6260
4.21k
    if (std::to_string(scheme::get_special_port(*processed_init->protocol)) ==
6261
4.21k
        port) {
6262
0
      processed_init->port->clear();
6263
0
    }
6264
4.21k
  }
6265
6266
  // Let urlPattern be a new URL pattern.
6267
4.21k
  url_pattern<regex_provider> url_pattern_{};
6268
6269
  // Set urlPattern's protocol component to the result of compiling a component
6270
  // given processedInit["protocol"], canonicalize a protocol, and default
6271
  // options.
6272
4.21k
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6273
4.21k
      processed_init->protocol.value(),
6274
4.21k
      url_pattern_helpers::canonicalize_protocol,
6275
4.21k
      url_pattern_compile_component_options::DEFAULT);
6276
4.21k
  if (!protocol_component) {
6277
0
    ada_log("url_pattern_component::compile failed for protocol ",
6278
0
            processed_init->protocol.value());
6279
0
    return tl::unexpected(protocol_component.error());
6280
0
  }
6281
4.21k
  url_pattern_.protocol_component = std::move(*protocol_component);
6282
6283
  // Set urlPattern's username component to the result of compiling a component
6284
  // given processedInit["username"], canonicalize a username, and default
6285
  // options.
6286
4.21k
  auto username_component = url_pattern_component<regex_provider>::compile(
6287
4.21k
      processed_init->username.value(),
6288
4.21k
      url_pattern_helpers::canonicalize_username,
6289
4.21k
      url_pattern_compile_component_options::DEFAULT);
6290
4.21k
  if (!username_component) {
6291
0
    ada_log("url_pattern_component::compile failed for username ",
6292
0
            processed_init->username.value());
6293
0
    return tl::unexpected(username_component.error());
6294
0
  }
6295
4.21k
  url_pattern_.username_component = std::move(*username_component);
6296
6297
  // Set urlPattern's password component to the result of compiling a component
6298
  // given processedInit["password"], canonicalize a password, and default
6299
  // options.
6300
4.21k
  auto password_component = url_pattern_component<regex_provider>::compile(
6301
4.21k
      processed_init->password.value(),
6302
4.21k
      url_pattern_helpers::canonicalize_password,
6303
4.21k
      url_pattern_compile_component_options::DEFAULT);
6304
4.21k
  if (!password_component) {
6305
0
    ada_log("url_pattern_component::compile failed for password ",
6306
0
            processed_init->password.value());
6307
0
    return tl::unexpected(password_component.error());
6308
0
  }
6309
4.21k
  url_pattern_.password_component = std::move(*password_component);
6310
6311
  // TODO: Optimization opportunity. The following if statement can be
6312
  // simplified.
6313
  // If the result running hostname pattern is an IPv6 address given
6314
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6315
  // to the result of compiling a component given processedInit["hostname"],
6316
  // canonicalize an IPv6 hostname, and hostname options.
6317
4.21k
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6318
0
    ada_log("processed_init->hostname is ipv6 address");
6319
    // then set urlPattern's hostname component to the result of compiling a
6320
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6321
    // and hostname options.
6322
0
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6323
0
        processed_init->hostname.value(),
6324
0
        url_pattern_helpers::canonicalize_ipv6_hostname,
6325
0
        url_pattern_compile_component_options::DEFAULT);
6326
0
    if (!hostname_component) {
6327
0
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6328
0
              processed_init->hostname.value());
6329
0
      return tl::unexpected(hostname_component.error());
6330
0
    }
6331
0
    url_pattern_.hostname_component = std::move(*hostname_component);
6332
4.21k
  } else {
6333
    // Otherwise, set urlPattern's hostname component to the result of compiling
6334
    // a component given processedInit["hostname"], canonicalize a hostname, and
6335
    // hostname options.
6336
4.21k
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6337
4.21k
        processed_init->hostname.value(),
6338
4.21k
        url_pattern_helpers::canonicalize_hostname,
6339
4.21k
        url_pattern_compile_component_options::HOSTNAME);
6340
4.21k
    if (!hostname_component) {
6341
0
      ada_log("url_pattern_component::compile failed for hostname ",
6342
0
              processed_init->hostname.value());
6343
0
      return tl::unexpected(hostname_component.error());
6344
0
    }
6345
4.21k
    url_pattern_.hostname_component = std::move(*hostname_component);
6346
4.21k
  }
6347
6348
  // Set urlPattern's port component to the result of compiling a component
6349
  // given processedInit["port"], canonicalize a port, and default options.
6350
4.21k
  auto port_component = url_pattern_component<regex_provider>::compile(
6351
4.21k
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6352
4.21k
      url_pattern_compile_component_options::DEFAULT);
6353
4.21k
  if (!port_component) {
6354
0
    ada_log("url_pattern_component::compile failed for port ",
6355
0
            processed_init->port.value());
6356
0
    return tl::unexpected(port_component.error());
6357
0
  }
6358
4.21k
  url_pattern_.port_component = std::move(*port_component);
6359
6360
  // Let compileOptions be a copy of the default options with the ignore case
6361
  // property set to options["ignoreCase"].
6362
4.21k
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6363
4.21k
  if (options) {
6364
1.40k
    compile_options.ignore_case = options->ignore_case;
6365
1.40k
  }
6366
6367
  // TODO: Optimization opportunity: Simplify this if statement.
6368
  // If the result of running protocol component matches a special scheme given
6369
  // urlPattern's protocol component is true, then:
6370
4.21k
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6371
4.21k
          regex_provider>(url_pattern_.protocol_component)) {
6372
    // Let pathCompileOptions be copy of the pathname options with the ignore
6373
    // case property set to options["ignoreCase"].
6374
4.21k
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6375
4.21k
    if (options) {
6376
1.40k
      path_compile_options.ignore_case = options->ignore_case;
6377
1.40k
    }
6378
6379
    // Set urlPattern's pathname component to the result of compiling a
6380
    // component given processedInit["pathname"], canonicalize a pathname, and
6381
    // pathCompileOptions.
6382
4.21k
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6383
4.21k
        processed_init->pathname.value(),
6384
4.21k
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6385
4.21k
    if (!pathname_component) {
6386
0
      ada_log("url_pattern_component::compile failed for pathname ",
6387
0
              processed_init->pathname.value());
6388
0
      return tl::unexpected(pathname_component.error());
6389
0
    }
6390
4.21k
    url_pattern_.pathname_component = std::move(*pathname_component);
6391
4.21k
  } else {
6392
    // Otherwise set urlPattern's pathname component to the result of compiling
6393
    // a component given processedInit["pathname"], canonicalize an opaque
6394
    // pathname, and compileOptions.
6395
0
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6396
0
        processed_init->pathname.value(),
6397
0
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6398
0
    if (!pathname_component) {
6399
0
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6400
0
              processed_init->pathname.value());
6401
0
      return tl::unexpected(pathname_component.error());
6402
0
    }
6403
0
    url_pattern_.pathname_component = std::move(*pathname_component);
6404
0
  }
6405
6406
  // Set urlPattern's search component to the result of compiling a component
6407
  // given processedInit["search"], canonicalize a search, and compileOptions.
6408
4.21k
  auto search_component = url_pattern_component<regex_provider>::compile(
6409
4.21k
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6410
4.21k
      compile_options);
6411
4.21k
  if (!search_component) {
6412
0
    ada_log("url_pattern_component::compile failed for search ",
6413
0
            processed_init->search.value());
6414
0
    return tl::unexpected(search_component.error());
6415
0
  }
6416
4.21k
  url_pattern_.search_component = std::move(*search_component);
6417
6418
  // Set urlPattern's hash component to the result of compiling a component
6419
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6420
4.21k
  auto hash_component = url_pattern_component<regex_provider>::compile(
6421
4.21k
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6422
4.21k
      compile_options);
6423
4.21k
  if (!hash_component) {
6424
0
    ada_log("url_pattern_component::compile failed for hash ",
6425
0
            processed_init->hash.value());
6426
0
    return tl::unexpected(hash_component.error());
6427
0
  }
6428
4.21k
  url_pattern_.hash_component = std::move(*hash_component);
6429
6430
  // Return urlPattern.
6431
4.21k
  return url_pattern_;
6432
4.21k
}
6433
#endif  // ADA_INCLUDE_URL_PATTERN
6434
6435
}  // namespace ada::parser
6436
6437
#endif  // ADA_PARSER_INL_H
6438
/* end file include/ada/parser-inl.h */
6439
/* begin file include/ada/scheme-inl.h */
6440
/**
6441
 * @file scheme-inl.h
6442
 * @brief Definitions for the URL scheme.
6443
 */
6444
#ifndef ADA_SCHEME_INL_H
6445
#define ADA_SCHEME_INL_H
6446
6447
6448
namespace ada::scheme {
6449
6450
/**
6451
 * @namespace ada::scheme::details
6452
 * @brief Includes the definitions for scheme specific entities
6453
 */
6454
namespace details {
6455
// for use with is_special and get_special_port
6456
// Spaces, if present, are removed from URL.
6457
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6458
                                                "ftp",  "wss", "file",  " "};
6459
// for use with get_special_port
6460
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6461
}  // namespace details
6462
6463
/****
6464
 * @private
6465
 * In is_special, get_scheme_type, and get_special_port, we
6466
 * use a standard hashing technique to find the index of the scheme in
6467
 * the is_special_list. The hashing technique is based on the size of
6468
 * the scheme and the first character of the scheme. It ensures that we
6469
 * do at most one string comparison per call. If the protocol is
6470
 * predictible (e.g., it is always "http"), we can get a better average
6471
 * performance by using a simpler approach where we loop and compare
6472
 * scheme with all possible protocols starting with the most likely
6473
 * protocol. Doing multiple comparisons may have a poor worst case
6474
 * performance, however. In this instance, we choose a potentially
6475
 * slightly lower best-case performance for a better worst-case
6476
 * performance. We can revisit this choice at any time.
6477
 *
6478
 * Reference:
6479
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6480
 * More C++ gems 17 (2000).
6481
 *
6482
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6483
 *
6484
 * Reference: https://github.com/ada-url/ada/issues/617
6485
 ****/
6486
6487
22.5k
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6488
22.5k
  if (scheme.empty()) {
6489
0
    return false;
6490
0
  }
6491
22.5k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6492
22.5k
  const std::string_view target = details::is_special_list[hash_value];
6493
22.5k
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6494
22.5k
}
6495
4.21k
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6496
4.21k
  if (scheme.empty()) {
6497
0
    return 0;
6498
0
  }
6499
4.21k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6500
4.21k
  const std::string_view target = details::is_special_list[hash_value];
6501
4.21k
  if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) {
6502
4.21k
    return details::special_ports[hash_value];
6503
4.21k
  } else {
6504
0
    return 0;
6505
0
  }
6506
4.21k
}
6507
16.2k
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6508
16.2k
  return details::special_ports[int(type)];
6509
16.2k
}
6510
118k
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6511
118k
  if (scheme.empty()) {
6512
0
    return ada::scheme::NOT_SPECIAL;
6513
0
  }
6514
118k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6515
118k
  const std::string_view target = details::is_special_list[hash_value];
6516
118k
  if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) {
6517
62.2k
    return ada::scheme::type(hash_value);
6518
62.2k
  } else {
6519
56.3k
    return ada::scheme::NOT_SPECIAL;
6520
56.3k
  }
6521
118k
}
6522
6523
}  // namespace ada::scheme
6524
6525
#endif  // ADA_SCHEME_INL_H
6526
/* end file include/ada/scheme-inl.h */
6527
/* begin file include/ada/serializers.h */
6528
/**
6529
 * @file serializers.h
6530
 * @brief IP address serialization utilities.
6531
 *
6532
 * This header provides functions for converting IP addresses to their
6533
 * string representations according to the WHATWG URL Standard.
6534
 */
6535
#ifndef ADA_SERIALIZERS_H
6536
#define ADA_SERIALIZERS_H
6537
6538
6539
#include <array>
6540
#include <string>
6541
6542
/**
6543
 * @namespace ada::serializers
6544
 * @brief IP address serialization functions.
6545
 *
6546
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6547
 */
6548
namespace ada::serializers {
6549
6550
/**
6551
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6552
 * Used for :: compression in IPv6 serialization.
6553
 *
6554
 * @param address The 8 16-bit pieces of the IPv6 address.
6555
 * @param[out] compress Index of the start of the longest zero sequence.
6556
 * @param[out] compress_length Length of the longest zero sequence.
6557
 */
6558
void find_longest_sequence_of_ipv6_pieces(
6559
    const std::array<uint16_t, 8>& address, size_t& compress,
6560
    size_t& compress_length) noexcept;
6561
6562
/**
6563
 * Serializes an IPv6 address to its string representation.
6564
 *
6565
 * @param address The 8 16-bit pieces of the IPv6 address.
6566
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6567
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6568
 */
6569
std::string ipv6(const std::array<uint16_t, 8>& address);
6570
6571
/**
6572
 * Serializes an IPv4 address to its dotted-decimal string representation.
6573
 *
6574
 * @param address The 32-bit IPv4 address as an integer.
6575
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6576
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6577
 */
6578
std::string ipv4(uint64_t address);
6579
6580
}  // namespace ada::serializers
6581
6582
#endif  // ADA_SERIALIZERS_H
6583
/* end file include/ada/serializers.h */
6584
/* begin file include/ada/state.h */
6585
/**
6586
 * @file state.h
6587
 * @brief URL parser state machine states.
6588
 *
6589
 * Defines the states used by the URL parsing state machine as specified
6590
 * in the WHATWG URL Standard.
6591
 *
6592
 * @see https://url.spec.whatwg.org/#url-parsing
6593
 */
6594
#ifndef ADA_STATE_H
6595
#define ADA_STATE_H
6596
6597
6598
#include <string>
6599
6600
namespace ada {
6601
6602
/**
6603
 * @brief States in the URL parsing state machine.
6604
 *
6605
 * The URL parser processes input through a sequence of states, each handling
6606
 * a specific part of the URL syntax.
6607
 *
6608
 * @see https://url.spec.whatwg.org/#url-parsing
6609
 */
6610
enum class state {
6611
  /**
6612
   * @see https://url.spec.whatwg.org/#authority-state
6613
   */
6614
  AUTHORITY,
6615
6616
  /**
6617
   * @see https://url.spec.whatwg.org/#scheme-start-state
6618
   */
6619
  SCHEME_START,
6620
6621
  /**
6622
   * @see https://url.spec.whatwg.org/#scheme-state
6623
   */
6624
  SCHEME,
6625
6626
  /**
6627
   * @see https://url.spec.whatwg.org/#host-state
6628
   */
6629
  HOST,
6630
6631
  /**
6632
   * @see https://url.spec.whatwg.org/#no-scheme-state
6633
   */
6634
  NO_SCHEME,
6635
6636
  /**
6637
   * @see https://url.spec.whatwg.org/#fragment-state
6638
   */
6639
  FRAGMENT,
6640
6641
  /**
6642
   * @see https://url.spec.whatwg.org/#relative-state
6643
   */
6644
  RELATIVE_SCHEME,
6645
6646
  /**
6647
   * @see https://url.spec.whatwg.org/#relative-slash-state
6648
   */
6649
  RELATIVE_SLASH,
6650
6651
  /**
6652
   * @see https://url.spec.whatwg.org/#file-state
6653
   */
6654
  FILE,
6655
6656
  /**
6657
   * @see https://url.spec.whatwg.org/#file-host-state
6658
   */
6659
  FILE_HOST,
6660
6661
  /**
6662
   * @see https://url.spec.whatwg.org/#file-slash-state
6663
   */
6664
  FILE_SLASH,
6665
6666
  /**
6667
   * @see https://url.spec.whatwg.org/#path-or-authority-state
6668
   */
6669
  PATH_OR_AUTHORITY,
6670
6671
  /**
6672
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
6673
   */
6674
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
6675
6676
  /**
6677
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
6678
   */
6679
  SPECIAL_AUTHORITY_SLASHES,
6680
6681
  /**
6682
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
6683
   */
6684
  SPECIAL_RELATIVE_OR_AUTHORITY,
6685
6686
  /**
6687
   * @see https://url.spec.whatwg.org/#query-state
6688
   */
6689
  QUERY,
6690
6691
  /**
6692
   * @see https://url.spec.whatwg.org/#path-state
6693
   */
6694
  PATH,
6695
6696
  /**
6697
   * @see https://url.spec.whatwg.org/#path-start-state
6698
   */
6699
  PATH_START,
6700
6701
  /**
6702
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
6703
   */
6704
  OPAQUE_PATH,
6705
6706
  /**
6707
   * @see https://url.spec.whatwg.org/#port-state
6708
   */
6709
  PORT,
6710
};
6711
6712
/**
6713
 * Converts a parser state to its string name for debugging.
6714
 * @param s The state to convert.
6715
 * @return A string representation of the state.
6716
 */
6717
ada_warn_unused std::string to_string(ada::state s);
6718
6719
}  // namespace ada
6720
6721
#endif  // ADA_STATE_H
6722
/* end file include/ada/state.h */
6723
/* begin file include/ada/unicode.h */
6724
/**
6725
 * @file unicode.h
6726
 * @brief Definitions for all unicode specific functions.
6727
 */
6728
#ifndef ADA_UNICODE_H
6729
#define ADA_UNICODE_H
6730
6731
6732
#include <string>
6733
#include <string_view>
6734
#include <optional>
6735
6736
/**
6737
 * Unicode operations. These functions are not part of our public API and may
6738
 * change at any time.
6739
 *
6740
 * @private
6741
 * @namespace ada::unicode
6742
 * @brief Includes the definitions for unicode operations
6743
 */
6744
namespace ada::unicode {
6745
6746
/**
6747
 * @private
6748
 * We receive a UTF-8 string representing a domain name.
6749
 * If the string is percent encoded, we apply percent decoding.
6750
 *
6751
 * Given a domain, we need to identify its labels.
6752
 * They are separated by label-separators:
6753
 *
6754
 * U+002E (.) FULL STOP
6755
 * U+FF0E FULLWIDTH FULL STOP
6756
 * U+3002 IDEOGRAPHIC FULL STOP
6757
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
6758
 *
6759
 * They are all mapped to U+002E.
6760
 *
6761
 * We process each label into a string that should not exceed 63 octets.
6762
 * If the string is already punycode (starts with "xn--"), then we must
6763
 * scan it to look for unallowed code points.
6764
 * Otherwise, if the string is not pure ASCII, we need to transcode it
6765
 * to punycode by following RFC 3454 which requires us to
6766
 * - Map characters  (see section 3),
6767
 * - Normalize (see section 4),
6768
 * - Reject forbidden characters,
6769
 * - Check for right-to-left characters and if so, check all requirements (see
6770
 * section 6),
6771
 * - Optionally reject based on unassigned code points (section 7).
6772
 *
6773
 * The Unicode standard provides a table of code points with a mapping, a list
6774
 * of forbidden code points and so forth. This table is subject to change and
6775
 * will vary based on the implementation. For Unicode 15, the table is at
6776
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
6777
 * If you use ICU, they parse this table and map it to code using a Python
6778
 * script.
6779
 *
6780
 * The resulting strings should not exceed 255 octets according to RFC 1035
6781
 * section 2.3.4. ICU checks for label size and domain size, but these errors
6782
 * are ignored.
6783
 *
6784
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
6785
 *
6786
 */
6787
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
6788
              size_t first_percent);
6789
6790
/**
6791
 * @private
6792
 * Checks if the input has tab or newline characters.
6793
 *
6794
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
6795
 * enough that compilers like GCC can 'autovectorize it'.
6796
 */
6797
ada_really_inline bool has_tabs_or_newline(
6798
    std::string_view user_input) noexcept;
6799
6800
/**
6801
 * @private
6802
 * Checks if the input is a forbidden host code point.
6803
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
6804
 */
6805
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
6806
6807
/**
6808
 * @private
6809
 * Checks if the input contains a forbidden domain code point.
6810
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6811
 */
6812
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
6813
    const char* input, size_t length) noexcept;
6814
6815
/**
6816
 * @private
6817
 * Checks if the input contains a forbidden domain code point in which case
6818
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
6819
 * then the second bit is set to 1.
6820
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6821
 */
6822
ada_really_inline constexpr uint8_t
6823
contains_forbidden_domain_code_point_or_upper(const char* input,
6824
                                              size_t length) noexcept;
6825
6826
/**
6827
 * @private
6828
 * Checks if the input is a forbidden domain code point.
6829
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6830
 */
6831
ada_really_inline constexpr bool is_forbidden_domain_code_point(
6832
    char c) noexcept;
6833
6834
/**
6835
 * @private
6836
 * Checks if the input is alphanumeric, '+', '-' or '.'
6837
 */
6838
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
6839
6840
/**
6841
 * @private
6842
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
6843
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
6844
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
6845
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
6846
 */
6847
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
6848
6849
/**
6850
 * @private
6851
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
6852
 * inclusive.
6853
 */
6854
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
6855
6856
/**
6857
 * @private
6858
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
6859
 * character.
6860
 */
6861
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
6862
6863
/**
6864
 * @private
6865
 * Checks if the input is a C0 control or space character.
6866
 *
6867
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
6868
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
6869
 * SEPARATOR ONE, inclusive.
6870
 */
6871
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
6872
6873
/**
6874
 * @private
6875
 * Checks if the input is a ASCII tab or newline character.
6876
 *
6877
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
6878
 */
6879
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
6880
6881
/**
6882
 * @private
6883
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
6884
 * match for ".%2e", "%2e.", or "%2e%2e".
6885
 */
6886
ada_really_inline constexpr bool is_double_dot_path_segment(
6887
    std::string_view input) noexcept;
6888
6889
/**
6890
 * @private
6891
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
6892
 * match for "%2e".
6893
 */
6894
ada_really_inline constexpr bool is_single_dot_path_segment(
6895
    std::string_view input) noexcept;
6896
6897
/**
6898
 * @private
6899
 * @details ipv4 character might contain 0-9 or a-f character ranges.
6900
 */
6901
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
6902
6903
/**
6904
 * @private
6905
 * @details Convert hex to binary. Caller is responsible to ensure that
6906
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
6907
 */
6908
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
6909
6910
/**
6911
 * @private
6912
 * first_percent should be  = input.find('%')
6913
 *
6914
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
6915
 * @author Node.js
6916
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
6917
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
6918
 */
6919
std::string percent_decode(std::string_view input, size_t first_percent);
6920
6921
/**
6922
 * @private
6923
 * Returns a percent-encoding string whether percent encoding was needed or not.
6924
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
6925
 */
6926
std::string percent_encode(std::string_view input,
6927
                           const uint8_t character_set[]);
6928
/**
6929
 * @private
6930
 * Returns a percent-encoded string version of input, while starting the percent
6931
 * encoding at the provided index.
6932
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
6933
 */
6934
std::string percent_encode(std::string_view input,
6935
                           const uint8_t character_set[], size_t index);
6936
/**
6937
 * @private
6938
 * Returns true if percent encoding was needed, in which case, we store
6939
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
6940
 * true, the content is appended to 'out'.
6941
 * If percent encoding is not needed, out is left unchanged.
6942
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
6943
 */
6944
template <bool append>
6945
bool percent_encode(std::string_view input, const uint8_t character_set[],
6946
                    std::string& out);
6947
/**
6948
 * @private
6949
 * Returns the index at which percent encoding should start, or (equivalently),
6950
 * the length of the prefix that does not require percent encoding.
6951
 */
6952
ada_really_inline size_t percent_encode_index(std::string_view input,
6953
                                              const uint8_t character_set[]);
6954
/**
6955
 * @private
6956
 * Lowers the string in-place, assuming that the content is ASCII.
6957
 * Return true if the content was ASCII.
6958
 */
6959
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
6960
}  // namespace ada::unicode
6961
6962
#endif  // ADA_UNICODE_H
6963
/* end file include/ada/unicode.h */
6964
/* begin file include/ada/url_base-inl.h */
6965
/**
6966
 * @file url_base-inl.h
6967
 * @brief Inline functions for url base
6968
 */
6969
#ifndef ADA_URL_BASE_INL_H
6970
#define ADA_URL_BASE_INL_H
6971
6972
6973
#include <string>
6974
#if ADA_REGULAR_VISUAL_STUDIO
6975
#include <intrin.h>
6976
#endif  // ADA_REGULAR_VISUAL_STUDIO
6977
6978
namespace ada {
6979
6980
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
6981
529k
    const noexcept {
6982
529k
  return type != ada::scheme::NOT_SPECIAL;
6983
529k
}
6984
6985
9.88k
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
6986
9.88k
  return ada::scheme::get_special_port(type);
6987
9.88k
}
6988
6989
[[nodiscard]] ada_really_inline uint16_t
6990
6.32k
url_base::scheme_default_port() const noexcept {
6991
6.32k
  return scheme::get_special_port(type);
6992
6.32k
}
6993
6994
}  // namespace ada
6995
6996
#endif  // ADA_URL_BASE_INL_H
6997
/* end file include/ada/url_base-inl.h */
6998
/* begin file include/ada/url-inl.h */
6999
/**
7000
 * @file url-inl.h
7001
 * @brief Definitions for the URL
7002
 */
7003
#ifndef ADA_URL_INL_H
7004
#define ADA_URL_INL_H
7005
7006
7007
#include <charconv>
7008
#include <optional>
7009
#include <string>
7010
#if ADA_REGULAR_VISUAL_STUDIO
7011
#include <intrin.h>
7012
#endif  // ADA_REGULAR_VISUAL_STUDIO
7013
7014
namespace ada {
7015
14.3k
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7016
14.3k
  return !username.empty() || !password.empty();
7017
14.3k
}
7018
0
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7019
0
  return port.has_value();
7020
0
}
7021
29.9k
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7022
29.9k
  return !host.has_value() || host.value().empty() ||
7023
29.9k
         type == ada::scheme::type::FILE;
7024
29.9k
}
7025
0
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7026
0
  if (!host.has_value()) {
7027
0
    return false;
7028
0
  }
7029
0
  return host.value().empty();
7030
0
}
7031
0
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7032
0
  return host.has_value();
7033
0
}
7034
0
inline std::ostream &operator<<(std::ostream &out, const ada::url &u) {
7035
0
  return out << u.to_string();
7036
0
}
7037
7038
0
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7039
0
  return path.size();
7040
0
}
7041
7042
9.00k
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7043
9.00k
  return path;
7044
9.00k
}
7045
7046
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7047
0
    const noexcept {
7048
0
  url_components out{};
7049
0
7050
0
  // protocol ends with ':'. for example: "https:"
7051
0
  out.protocol_end = uint32_t(get_protocol().size());
7052
0
7053
0
  // Trailing index is always the next character of the current one.
7054
0
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7055
0
  size_t running_index = out.protocol_end;
7056
0
7057
0
  if (host.has_value()) {
7058
0
    // 2 characters for "//" and 1 character for starting index
7059
0
    out.host_start = out.protocol_end + 2;
7060
0
7061
0
    if (has_credentials()) {
7062
0
      out.username_end = uint32_t(out.host_start + username.size());
7063
0
7064
0
      out.host_start += uint32_t(username.size());
7065
0
7066
0
      if (!password.empty()) {
7067
0
        out.host_start += uint32_t(password.size() + 1);
7068
0
      }
7069
0
7070
0
      out.host_end = uint32_t(out.host_start + host.value().size());
7071
0
    } else {
7072
0
      out.username_end = out.host_start;
7073
0
7074
0
      // Host does not start with "@" if it does not include credentials.
7075
0
      out.host_end = uint32_t(out.host_start + host.value().size()) - 1;
7076
0
    }
7077
0
7078
0
    running_index = out.host_end + 1;
7079
0
  } else {
7080
0
    // Update host start and end date to the same index, since it does not
7081
0
    // exist.
7082
0
    out.host_start = out.protocol_end;
7083
0
    out.host_end = out.host_start;
7084
0
7085
0
    if (!has_opaque_path && path.starts_with("//")) {
7086
0
      // If url's host is null, url does not have an opaque path, url's path's
7087
0
      // size is greater than 1, and url's path[0] is the empty string, then
7088
0
      // append U+002F (/) followed by U+002E (.) to output.
7089
0
      running_index = out.protocol_end + 2;
7090
0
    } else {
7091
0
      running_index = out.protocol_end;
7092
0
    }
7093
0
  }
7094
0
7095
0
  if (port.has_value()) {
7096
0
    out.port = *port;
7097
0
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7098
0
  }
7099
0
7100
0
  out.pathname_start = uint32_t(running_index);
7101
0
7102
0
  running_index += path.size();
7103
0
7104
0
  if (query.has_value()) {
7105
0
    out.search_start = uint32_t(running_index);
7106
0
    running_index += get_search().size();
7107
0
    if (get_search().empty()) {
7108
0
      running_index++;
7109
0
    }
7110
0
  }
7111
0
7112
0
  if (hash.has_value()) {
7113
0
    out.hash_start = uint32_t(running_index);
7114
0
  }
7115
0
7116
0
  return out;
7117
0
}
7118
7119
44
inline void url::update_base_hostname(std::string_view input) { host = input; }
7120
7121
222
inline void url::update_unencoded_base_hash(std::string_view input) {
7122
  // We do the percent encoding
7123
222
  hash = unicode::percent_encode(input,
7124
222
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7125
222
}
7126
7127
inline void url::update_base_search(std::string_view input,
7128
333
                                    const uint8_t query_percent_encode_set[]) {
7129
333
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7130
333
}
7131
7132
0
inline void url::update_base_search(std::optional<std::string> &&input) {
7133
0
  query = std::move(input);
7134
0
}
7135
7136
12.6k
inline void url::update_base_pathname(const std::string_view input) {
7137
12.6k
  path = input;
7138
12.6k
}
7139
7140
0
inline void url::update_base_username(const std::string_view input) {
7141
0
  username = input;
7142
0
}
7143
7144
0
inline void url::update_base_password(const std::string_view input) {
7145
0
  password = input;
7146
0
}
7147
7148
5.20k
inline void url::update_base_port(std::optional<uint16_t> input) {
7149
5.20k
  port = input;
7150
5.20k
}
7151
7152
0
constexpr void url::clear_pathname() { path.clear(); }
7153
7154
0
constexpr void url::clear_search() { query = std::nullopt; }
7155
7156
0
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7157
0
  return hash.has_value();
7158
0
}
7159
7160
6.59k
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7161
6.59k
  return query.has_value();
7162
6.59k
}
7163
7164
1.44k
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7165
7166
4.72k
inline void url::set_scheme(std::string &&new_scheme) noexcept {
7167
4.72k
  type = ada::scheme::get_scheme_type(new_scheme);
7168
  // We only move the 'scheme' if it is non-special.
7169
4.72k
  if (!is_special()) {
7170
2.96k
    non_special_scheme = std::move(new_scheme);
7171
2.96k
  }
7172
4.72k
}
7173
7174
0
constexpr void url::copy_scheme(ada::url &&u) {
7175
0
  non_special_scheme = u.non_special_scheme;
7176
0
  type = u.type;
7177
0
}
7178
7179
0
constexpr void url::copy_scheme(const ada::url &u) {
7180
0
  non_special_scheme = u.non_special_scheme;
7181
0
  type = u.type;
7182
0
}
7183
7184
9.26k
[[nodiscard]] ada_really_inline std::string url::get_href() const {
7185
9.26k
  std::string output = get_protocol();
7186
7187
9.26k
  if (host.has_value()) {
7188
5.48k
    output += "//";
7189
5.48k
    if (has_credentials()) {
7190
776
      output += username;
7191
776
      if (!password.empty()) {
7192
315
        output += ":" + get_password();
7193
315
      }
7194
776
      output += "@";
7195
776
    }
7196
5.48k
    output += host.value();
7197
5.48k
    if (port.has_value()) {
7198
204
      output += ":" + get_port();
7199
204
    }
7200
5.48k
  } else if (!has_opaque_path && path.starts_with("//")) {
7201
    // If url's host is null, url does not have an opaque path, url's path's
7202
    // size is greater than 1, and url's path[0] is the empty string, then
7203
    // append U+002F (/) followed by U+002E (.) to output.
7204
39
    output += "/.";
7205
39
  }
7206
9.26k
  output += path;
7207
9.26k
  if (query.has_value()) {
7208
496
    output += "?" + query.value();
7209
496
  }
7210
9.26k
  if (hash.has_value()) {
7211
330
    output += "#" + hash.value();
7212
330
  }
7213
9.26k
  return output;
7214
9.26k
}
7215
7216
ada_really_inline size_t url::parse_port(std::string_view view,
7217
2.33k
                                         bool check_trailing_content) noexcept {
7218
2.33k
  ada_log("parse_port('", view, "') ", view.size());
7219
2.33k
  if (!view.empty() && view[0] == '-') {
7220
2
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7221
2
    is_valid = false;
7222
2
    return 0;
7223
2
  }
7224
2.33k
  uint16_t parsed_port{};
7225
2.33k
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7226
2.33k
  if (r.ec == std::errc::result_out_of_range) {
7227
222
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7228
222
    is_valid = false;
7229
222
    return 0;
7230
222
  }
7231
2.11k
  ada_log("parse_port: ", parsed_port);
7232
2.11k
  const auto consumed = size_t(r.ptr - view.data());
7233
2.11k
  ada_log("parse_port: consumed ", consumed);
7234
2.11k
  if (check_trailing_content) {
7235
635
    is_valid &=
7236
635
        (consumed == view.size() || view[consumed] == '/' ||
7237
115
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7238
635
  }
7239
2.11k
  ada_log("parse_port: is_valid = ", is_valid);
7240
2.11k
  if (is_valid) {
7241
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7242
1.70k
    auto default_port = scheme_default_port();
7243
1.70k
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7244
1.69k
                         (default_port != parsed_port);
7245
1.70k
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7246
1.70k
                                                  : std::nullopt;
7247
1.70k
  }
7248
2.11k
  return consumed;
7249
2.33k
}
7250
7251
}  // namespace ada
7252
7253
#endif  // ADA_URL_H
7254
/* end file include/ada/url-inl.h */
7255
/* begin file include/ada/url_components-inl.h */
7256
/**
7257
 * @file url_components.h
7258
 * @brief Declaration for the URL Components
7259
 */
7260
#ifndef ADA_URL_COMPONENTS_INL_H
7261
#define ADA_URL_COMPONENTS_INL_H
7262
7263
7264
namespace ada {
7265
7266
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7267
0
    const noexcept {
7268
0
  /**
7269
0
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7270
0
   *       |     |    |          | ^^^^|       |   |
7271
0
   *       |     |    |          | |   |       |   `----- hash_start
7272
0
   *       |     |    |          | |   |       `--------- search_start
7273
0
   *       |     |    |          | |   `----------------- pathname_start
7274
0
   *       |     |    |          | `--------------------- port
7275
0
   *       |     |    |          `----------------------- host_end
7276
0
   *       |     |    `---------------------------------- host_start
7277
0
   *       |     `--------------------------------------- username_end
7278
0
   *       `--------------------------------------------- protocol_end
7279
0
   */
7280
0
  // These conditions can be made more strict.
7281
0
  if (protocol_end == url_components::omitted) {
7282
0
    return false;
7283
0
  }
7284
0
  uint32_t index = protocol_end;
7285
0
7286
0
  if (username_end == url_components::omitted) {
7287
0
    return false;
7288
0
  }
7289
0
  if (username_end < index) {
7290
0
    return false;
7291
0
  }
7292
0
  index = username_end;
7293
0
7294
0
  if (host_start == url_components::omitted) {
7295
0
    return false;
7296
0
  }
7297
0
  if (host_start < index) {
7298
0
    return false;
7299
0
  }
7300
0
  index = host_start;
7301
0
7302
0
  if (port != url_components::omitted) {
7303
0
    if (port > 0xffff) {
7304
0
      return false;
7305
0
    }
7306
0
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7307
0
    if (index + port_length < index) {
7308
0
      return false;
7309
0
    }
7310
0
    index += port_length;
7311
0
  }
7312
0
7313
0
  if (pathname_start == url_components::omitted) {
7314
0
    return false;
7315
0
  }
7316
0
  if (pathname_start < index) {
7317
0
    return false;
7318
0
  }
7319
0
  index = pathname_start;
7320
0
7321
0
  if (search_start != url_components::omitted) {
7322
0
    if (search_start < index) {
7323
0
      return false;
7324
0
    }
7325
0
    index = search_start;
7326
0
  }
7327
0
7328
0
  if (hash_start != url_components::omitted) {
7329
0
    if (hash_start < index) {
7330
0
      return false;
7331
0
    }
7332
0
  }
7333
0
7334
0
  return true;
7335
0
}
7336
7337
}  // namespace ada
7338
#endif
7339
/* end file include/ada/url_components-inl.h */
7340
/* begin file include/ada/url_aggregator.h */
7341
/**
7342
 * @file url_aggregator.h
7343
 * @brief Declaration for the `ada::url_aggregator` class.
7344
 *
7345
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7346
 * URL using a single buffer with component offsets. This is the default and
7347
 * most memory-efficient URL representation in Ada.
7348
 *
7349
 * @see url.h for an alternative representation using separate strings
7350
 */
7351
#ifndef ADA_URL_AGGREGATOR_H
7352
#define ADA_URL_AGGREGATOR_H
7353
7354
#include <ostream>
7355
#include <string>
7356
#include <string_view>
7357
#include <variant>
7358
7359
7360
namespace ada {
7361
7362
namespace parser {}
7363
7364
/**
7365
 * @brief Memory-efficient URL representation using a single buffer.
7366
 *
7367
 * The `url_aggregator` stores the entire normalized URL in a single string
7368
 * buffer and tracks component boundaries using offsets. This design minimizes
7369
 * memory allocations and is ideal for read-mostly access patterns.
7370
 *
7371
 * Getter methods return `std::string_view` pointing into the internal buffer.
7372
 * These views are lightweight (no allocation) but become invalid if the
7373
 * url_aggregator is modified or destroyed.
7374
 *
7375
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7376
 * when any setter is called. Do not use a getter's result as input to a
7377
 * setter on the same object without copying first.
7378
 *
7379
 * @note This is the default URL type returned by `ada::parse()`.
7380
 *
7381
 * @see url For an alternative using separate std::string instances
7382
 */
7383
struct url_aggregator : url_base {
7384
89.9k
  url_aggregator() = default;
7385
0
  url_aggregator(const url_aggregator &u) = default;
7386
34.4k
  url_aggregator(url_aggregator &&u) noexcept = default;
7387
7.06k
  url_aggregator &operator=(url_aggregator &&u) noexcept = default;
7388
9.59k
  url_aggregator &operator=(const url_aggregator &u) = default;
7389
124k
  ~url_aggregator() override = default;
7390
7391
  /**
7392
   * The setter functions follow the steps defined in the URL Standard.
7393
   *
7394
   * The url_aggregator has a single buffer that contains the entire normalized
7395
   * URL. The various components are represented as offsets into that buffer.
7396
   * When you call get_pathname(), for example, you get a std::string_view that
7397
   * points into that buffer. If the url_aggregator is modified, the buffer may
7398
   * be reallocated, and the std::string_view you obtained earlier may become
7399
   * invalid. In particular, this implies that you cannot modify the URL using
7400
   * a setter function with a std::string_view that points into the
7401
   * url_aggregator E.g., the following is incorrect:
7402
   * url->set_hostname(url->get_pathname()).
7403
   * You must first copy the pathname to a separate string.
7404
   * std::string pathname(url->get_pathname());
7405
   * url->set_hostname(pathname);
7406
   *
7407
   * The caller is responsible for ensuring that the url_aggregator is not
7408
   * modified while any std::string_view obtained from it is in use.
7409
   */
7410
  bool set_href(std::string_view input);
7411
  bool set_host(std::string_view input);
7412
  bool set_hostname(std::string_view input);
7413
  bool set_protocol(std::string_view input);
7414
  bool set_username(std::string_view input);
7415
  bool set_password(std::string_view input);
7416
  bool set_port(std::string_view input);
7417
  bool set_pathname(std::string_view input);
7418
  void set_search(std::string_view input);
7419
  void set_hash(std::string_view input);
7420
7421
  /**
7422
   * Validates whether the hostname is a valid domain according to RFC 1034.
7423
   * @return `true` if the domain is valid, `false` otherwise.
7424
   */
7425
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7426
7427
  /**
7428
   * Returns the URL's origin (scheme + host + port for special URLs).
7429
   * @return A newly allocated string containing the serialized origin.
7430
   * @see https://url.spec.whatwg.org/#concept-url-origin
7431
   */
7432
  [[nodiscard]] std::string get_origin() const override;
7433
7434
  /**
7435
   * Returns the full serialized URL (the href) as a string_view.
7436
   * Does not allocate memory. The returned view becomes invalid if this
7437
   * url_aggregator is modified or destroyed.
7438
   * @return A string_view into the internal buffer.
7439
   * @see https://url.spec.whatwg.org/#dom-url-href
7440
   */
7441
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7442
      ada_lifetime_bound;
7443
7444
  /**
7445
   * Returns the URL's username component.
7446
   * Does not allocate memory. The returned view becomes invalid if this
7447
   * url_aggregator is modified or destroyed.
7448
   * @return A string_view of the username.
7449
   * @see https://url.spec.whatwg.org/#dom-url-username
7450
   */
7451
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
7452
7453
  /**
7454
   * Returns the URL's password component.
7455
   * Does not allocate memory. The returned view becomes invalid if this
7456
   * url_aggregator is modified or destroyed.
7457
   * @return A string_view of the password.
7458
   * @see https://url.spec.whatwg.org/#dom-url-password
7459
   */
7460
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
7461
7462
  /**
7463
   * Returns the URL's port as a string (e.g., "8080").
7464
   * Does not allocate memory. Returns empty view if no port is set.
7465
   * The returned view becomes invalid if this url_aggregator is modified.
7466
   * @return A string_view of the port.
7467
   * @see https://url.spec.whatwg.org/#dom-url-port
7468
   */
7469
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
7470
7471
  /**
7472
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
7473
   * Does not allocate memory. Returns empty view if no fragment is set.
7474
   * The returned view becomes invalid if this url_aggregator is modified.
7475
   * @return A string_view of the hash.
7476
   * @see https://url.spec.whatwg.org/#dom-url-hash
7477
   */
7478
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
7479
7480
  /**
7481
   * Returns the URL's host and port (e.g., "example.com:8080").
7482
   * Does not allocate memory. Returns empty view if no host is set.
7483
   * The returned view becomes invalid if this url_aggregator is modified.
7484
   * @return A string_view of host:port.
7485
   * @see https://url.spec.whatwg.org/#dom-url-host
7486
   */
7487
  [[nodiscard]] std::string_view get_host() const ada_lifetime_bound;
7488
7489
  /**
7490
   * Returns the URL's hostname (without port).
7491
   * Does not allocate memory. Returns empty view if no host is set.
7492
   * The returned view becomes invalid if this url_aggregator is modified.
7493
   * @return A string_view of the hostname.
7494
   * @see https://url.spec.whatwg.org/#dom-url-hostname
7495
   */
7496
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
7497
7498
  /**
7499
   * Returns the URL's path component.
7500
   * Does not allocate memory. The returned view becomes invalid if this
7501
   * url_aggregator is modified or destroyed.
7502
   * @return A string_view of the pathname.
7503
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7504
   */
7505
  [[nodiscard]] constexpr std::string_view get_pathname() const
7506
      ada_lifetime_bound;
7507
7508
  /**
7509
   * Returns the byte length of the pathname without creating a string.
7510
   * @return Size of the pathname in bytes.
7511
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7512
   */
7513
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
7514
7515
  /**
7516
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
7517
   * Does not allocate memory. Returns empty view if no query is set.
7518
   * The returned view becomes invalid if this url_aggregator is modified.
7519
   * @return A string_view of the search/query.
7520
   * @see https://url.spec.whatwg.org/#dom-url-search
7521
   */
7522
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
7523
7524
  /**
7525
   * Returns the URL's scheme followed by a colon (e.g., "https:").
7526
   * Does not allocate memory. The returned view becomes invalid if this
7527
   * url_aggregator is modified or destroyed.
7528
   * @return A string_view of the protocol.
7529
   * @see https://url.spec.whatwg.org/#dom-url-protocol
7530
   */
7531
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
7532
7533
  /**
7534
   * Checks if the URL has credentials (non-empty username or password).
7535
   * @return `true` if username or password is non-empty, `false` otherwise.
7536
   */
7537
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
7538
      const noexcept;
7539
7540
  /**
7541
   * Returns the URL component offsets for efficient serialization.
7542
   *
7543
   * The components represent byte offsets into the serialized URL:
7544
   * ```
7545
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7546
   *       |     |    |          | ^^^^|       |   |
7547
   *       |     |    |          | |   |       |   `----- hash_start
7548
   *       |     |    |          | |   |       `--------- search_start
7549
   *       |     |    |          | |   `----------------- pathname_start
7550
   *       |     |    |          | `--------------------- port
7551
   *       |     |    |          `----------------------- host_end
7552
   *       |     |    `---------------------------------- host_start
7553
   *       |     `--------------------------------------- username_end
7554
   *       `--------------------------------------------- protocol_end
7555
   * ```
7556
   * @return A constant reference to the url_components struct.
7557
   * @see https://github.com/servo/rust-url
7558
   */
7559
  [[nodiscard]] ada_really_inline const url_components &get_components()
7560
      const noexcept;
7561
7562
  /**
7563
   * Returns a JSON string representation of this URL for debugging.
7564
   * @return A JSON-formatted string with all URL components.
7565
   */
7566
  [[nodiscard]] std::string to_string() const override;
7567
7568
  /**
7569
   * Returns a visual diagram showing component boundaries in the URL.
7570
   * Useful for debugging and understanding URL structure.
7571
   * @return A multi-line string diagram.
7572
   */
7573
  [[nodiscard]] std::string to_diagram() const;
7574
7575
  /**
7576
   * Validates internal consistency of component offsets (for debugging).
7577
   * @return `true` if offsets are consistent, `false` if corrupted.
7578
   */
7579
  [[nodiscard]] constexpr bool validate() const noexcept;
7580
7581
  /**
7582
   * Checks if the URL has an empty hostname (host is set but empty string).
7583
   * @return `true` if host exists but is empty, `false` otherwise.
7584
   */
7585
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
7586
7587
  /**
7588
   * Checks if the URL has a hostname (including empty hostnames).
7589
   * @return `true` if host is present, `false` otherwise.
7590
   */
7591
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
7592
7593
  /**
7594
   * Checks if the URL has a non-empty username.
7595
   * @return `true` if username is non-empty, `false` otherwise.
7596
   */
7597
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
7598
7599
  /**
7600
   * Checks if the URL has a non-empty password.
7601
   * @return `true` if password is non-empty, `false` otherwise.
7602
   */
7603
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
7604
7605
  /**
7606
   * Checks if the URL has a non-default port explicitly specified.
7607
   * @return `true` if a port is present, `false` otherwise.
7608
   */
7609
  [[nodiscard]] constexpr bool has_port() const noexcept;
7610
7611
  /**
7612
   * Checks if the URL has a password component (may be empty).
7613
   * @return `true` if password is present, `false` otherwise.
7614
   */
7615
  [[nodiscard]] constexpr bool has_password() const noexcept;
7616
7617
  /**
7618
   * Checks if the URL has a fragment/hash component.
7619
   * @return `true` if hash is present, `false` otherwise.
7620
   */
7621
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
7622
7623
  /**
7624
   * Checks if the URL has a query/search component.
7625
   * @return `true` if query is present, `false` otherwise.
7626
   */
7627
  [[nodiscard]] constexpr bool has_search() const noexcept override;
7628
7629
  /**
7630
   * Removes the port from the URL.
7631
   */
7632
  inline void clear_port();
7633
7634
  /**
7635
   * Removes the hash/fragment from the URL.
7636
   */
7637
  inline void clear_hash();
7638
7639
  /**
7640
   * Removes the query/search string from the URL.
7641
   */
7642
  inline void clear_search() override;
7643
7644
 private:
7645
  // helper methods
7646
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
7647
      url_aggregator &url);
7648
  // parse_url methods
7649
  friend url_aggregator parser::parse_url<url_aggregator>(
7650
      std::string_view, const url_aggregator *);
7651
7652
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
7653
      std::string_view, const url_aggregator *);
7654
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
7655
      std::string_view, const url_aggregator *);
7656
7657
#if ADA_INCLUDE_URL_PATTERN
7658
  // url_pattern methods
7659
  template <url_pattern_regex::regex_concept regex_provider>
7660
  friend tl::expected<url_pattern<regex_provider>, errors>
7661
  parse_url_pattern_impl(
7662
      std::variant<std::string_view, url_pattern_init> &&input,
7663
      const std::string_view *base_url, const url_pattern_options *options);
7664
#endif  // ADA_INCLUDE_URL_PATTERN
7665
7666
  std::string buffer{};
7667
  url_components components{};
7668
7669
  /**
7670
   * Returns true if neither the search, nor the hash nor the pathname
7671
   * have been set.
7672
   * @return true if the buffer is ready to receive the path.
7673
   */
7674
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
7675
7676
  inline void add_authority_slashes_if_needed();
7677
7678
  /**
7679
   * To optimize performance, you may indicate how much memory to allocate
7680
   * within this instance.
7681
   */
7682
  constexpr void reserve(uint32_t capacity);
7683
7684
  ada_really_inline size_t parse_port(std::string_view view,
7685
                                      bool check_trailing_content) override;
7686
7687
2.59k
  ada_really_inline size_t parse_port(std::string_view view) override {
7688
2.59k
    return this->parse_port(view, false);
7689
2.59k
  }
7690
7691
  /**
7692
   * Return true on success. The 'in_place' parameter indicates whether the
7693
   * the string_view input is pointing in the buffer. When in_place is false,
7694
   * we must nearly always update the buffer.
7695
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
7696
   */
7697
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
7698
7699
  /**
7700
   * Return true on success.
7701
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
7702
   */
7703
  [[nodiscard]] bool parse_ipv6(std::string_view input);
7704
7705
  /**
7706
   * Return true on success.
7707
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
7708
   */
7709
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
7710
7711
  ada_really_inline void parse_path(std::string_view input);
7712
7713
  /**
7714
   * A URL cannot have a username/password/port if its host is null or the empty
7715
   * string, or its scheme is "file".
7716
   */
7717
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
7718
7719
  template <bool override_hostname = false>
7720
  bool set_host_or_hostname(std::string_view input);
7721
7722
  ada_really_inline bool parse_host(std::string_view input);
7723
7724
  inline void update_base_authority(std::string_view base_buffer,
7725
                                    const url_components &base);
7726
  inline void update_unencoded_base_hash(std::string_view input);
7727
  inline void update_base_hostname(std::string_view input);
7728
  inline void update_base_search(std::string_view input);
7729
  inline void update_base_search(std::string_view input,
7730
                                 const uint8_t *query_percent_encode_set);
7731
  inline void update_base_pathname(std::string_view input);
7732
  inline void update_base_username(std::string_view input);
7733
  inline void append_base_username(std::string_view input);
7734
  inline void update_base_password(std::string_view input);
7735
  inline void append_base_password(std::string_view input);
7736
  inline void update_base_port(uint32_t input);
7737
  inline void append_base_pathname(std::string_view input);
7738
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
7739
  constexpr void clear_hostname();
7740
  constexpr void clear_password();
7741
  constexpr void clear_pathname() override;
7742
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
7743
  void delete_dash_dot();
7744
  inline void consume_prepared_path(std::string_view input);
7745
  template <bool has_state_override = false>
7746
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
7747
      std::string_view input);
7748
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
7749
                                                std::string_view input);
7750
  [[nodiscard]] constexpr bool has_authority() const noexcept;
7751
  constexpr void set_protocol_as_file();
7752
  inline void set_scheme(std::string_view new_scheme);
7753
  /**
7754
   * Fast function to set the scheme from a view with a colon in the
7755
   * buffer, does not change type.
7756
   */
7757
  inline void set_scheme_from_view_with_colon(
7758
      std::string_view new_scheme_with_colon);
7759
  inline void copy_scheme(const url_aggregator &u);
7760
7761
  inline void update_host_to_base_host(const std::string_view input);
7762
7763
};  // url_aggregator
7764
7765
inline std::ostream &operator<<(std::ostream &out, const url &u);
7766
}  // namespace ada
7767
7768
#endif
7769
/* end file include/ada/url_aggregator.h */
7770
/* begin file include/ada/url_aggregator-inl.h */
7771
/**
7772
 * @file url_aggregator-inl.h
7773
 * @brief Inline functions for url aggregator
7774
 */
7775
#ifndef ADA_URL_AGGREGATOR_INL_H
7776
#define ADA_URL_AGGREGATOR_INL_H
7777
7778
/* begin file include/ada/unicode-inl.h */
7779
/**
7780
 * @file unicode-inl.h
7781
 * @brief Definitions for unicode operations.
7782
 */
7783
#ifndef ADA_UNICODE_INL_H
7784
#define ADA_UNICODE_INL_H
7785
7786
/**
7787
 * Unicode operations. These functions are not part of our public API and may
7788
 * change at any time.
7789
 *
7790
 * private
7791
 * @namespace ada::unicode
7792
 * @brief Includes the declarations for unicode operations
7793
 */
7794
namespace ada::unicode {
7795
ada_really_inline size_t percent_encode_index(const std::string_view input,
7796
27.9k
                                              const uint8_t character_set[]) {
7797
27.9k
  const char* data = input.data();
7798
27.9k
  const size_t size = input.size();
7799
7800
  // Process 8 bytes at a time using unrolled loop
7801
27.9k
  size_t i = 0;
7802
42.2k
  for (; i + 8 <= size; i += 8) {
7803
30.3k
    unsigned char chunk[8];
7804
30.3k
    std::memcpy(&chunk, data + i,
7805
30.3k
                8);  // entices compiler to unconditionally process 8 characters
7806
7807
    // Check 8 characters at once
7808
171k
    for (size_t j = 0; j < 8; j++) {
7809
156k
      if (character_sets::bit_at(character_set, chunk[j])) {
7810
16.0k
        return i + j;
7811
16.0k
      }
7812
156k
    }
7813
30.3k
  }
7814
7815
  // Handle remaining bytes
7816
31.1k
  for (; i < size; i++) {
7817
27.4k
    if (character_sets::bit_at(character_set, data[i])) {
7818
8.25k
      return i;
7819
8.25k
    }
7820
27.4k
  }
7821
7822
3.70k
  return size;
7823
11.9k
}
7824
}  // namespace ada::unicode
7825
7826
#endif  // ADA_UNICODE_INL_H
7827
/* end file include/ada/unicode-inl.h */
7828
7829
#include <charconv>
7830
#include <ostream>
7831
#include <string_view>
7832
7833
namespace ada {
7834
7835
inline void url_aggregator::update_base_authority(
7836
326
    std::string_view base_buffer, const ada::url_components &base) {
7837
326
  std::string_view input = base_buffer.substr(
7838
326
      base.protocol_end, base.host_start - base.protocol_end);
7839
326
  ada_log("url_aggregator::update_base_authority ", input);
7840
7841
326
  bool input_starts_with_dash = input.starts_with("//");
7842
326
  uint32_t diff = components.host_start - components.protocol_end;
7843
7844
326
  buffer.erase(components.protocol_end,
7845
326
               components.host_start - components.protocol_end);
7846
326
  components.username_end = components.protocol_end;
7847
7848
326
  if (input_starts_with_dash) {
7849
203
    input.remove_prefix(2);
7850
203
    diff += 2;  // add "//"
7851
203
    buffer.insert(components.protocol_end, "//");
7852
203
    components.username_end += 2;
7853
203
  }
7854
7855
326
  size_t password_delimiter = input.find(':');
7856
7857
  // Check if input contains both username and password by checking the
7858
  // delimiter: ":" A typical input that contains authority would be "user:pass"
7859
326
  if (password_delimiter != std::string_view::npos) {
7860
    // Insert both username and password
7861
32
    std::string_view username = input.substr(0, password_delimiter);
7862
32
    std::string_view password = input.substr(password_delimiter + 1);
7863
7864
32
    buffer.insert(components.protocol_end + diff, username);
7865
32
    diff += uint32_t(username.size());
7866
32
    buffer.insert(components.protocol_end + diff, ":");
7867
32
    components.username_end = components.protocol_end + diff;
7868
32
    buffer.insert(components.protocol_end + diff + 1, password);
7869
32
    diff += uint32_t(password.size()) + 1;
7870
294
  } else if (!input.empty()) {
7871
    // Insert only username
7872
21
    buffer.insert(components.protocol_end + diff, input);
7873
21
    components.username_end =
7874
21
        components.protocol_end + diff + uint32_t(input.size());
7875
21
    diff += uint32_t(input.size());
7876
21
  }
7877
7878
326
  components.host_start += diff;
7879
7880
326
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
7881
0
    buffer.insert(components.host_start, "@");
7882
0
    diff++;
7883
0
  }
7884
326
  components.host_end += diff;
7885
326
  components.pathname_start += diff;
7886
326
  if (components.search_start != url_components::omitted) {
7887
0
    components.search_start += diff;
7888
0
  }
7889
326
  if (components.hash_start != url_components::omitted) {
7890
0
    components.hash_start += diff;
7891
0
  }
7892
326
}
7893
7894
16.5k
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
7895
16.5k
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
7896
16.5k
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
7897
16.5k
          " bytes] components.hash_start = ", components.hash_start);
7898
16.5k
  ADA_ASSERT_TRUE(validate());
7899
16.5k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
7900
16.5k
  if (components.hash_start != url_components::omitted) {
7901
360
    buffer.resize(components.hash_start);
7902
360
  }
7903
16.5k
  components.hash_start = uint32_t(buffer.size());
7904
16.5k
  buffer += "#";
7905
16.5k
  bool encoding_required = unicode::percent_encode<true>(
7906
16.5k
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
7907
  // When encoding_required is false, then buffer is left unchanged, and percent
7908
  // encoding was not deemed required.
7909
16.5k
  if (!encoding_required) {
7910
7.40k
    buffer.append(input);
7911
7.40k
  }
7912
16.5k
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
7913
16.5k
          buffer, "' [", buffer.size(), " bytes]");
7914
16.5k
  ADA_ASSERT_TRUE(validate());
7915
16.5k
}
7916
7917
ada_really_inline uint32_t url_aggregator::replace_and_resize(
7918
118k
    uint32_t start, uint32_t end, std::string_view input) {
7919
118k
  uint32_t current_length = end - start;
7920
118k
  uint32_t input_size = uint32_t(input.size());
7921
118k
  uint32_t new_difference = input_size - current_length;
7922
7923
118k
  if (current_length == 0) {
7924
95.1k
    buffer.insert(start, input);
7925
95.1k
  } else if (input_size == current_length) {
7926
3.96k
    buffer.replace(start, input_size, input);
7927
19.5k
  } else if (input_size < current_length) {
7928
13.3k
    buffer.erase(start, current_length - input_size);
7929
13.3k
    buffer.replace(start, input_size, input);
7930
13.3k
  } else {
7931
6.13k
    buffer.replace(start, current_length, input.substr(0, current_length));
7932
6.13k
    buffer.insert(start + current_length, input.substr(current_length));
7933
6.13k
  }
7934
7935
118k
  return new_difference;
7936
118k
}
7937
7938
62.5k
inline void url_aggregator::update_base_hostname(const std::string_view input) {
7939
62.5k
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
7940
62.5k
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
7941
62.5k
  ADA_ASSERT_TRUE(validate());
7942
62.5k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
7943
7944
  // This next line is required for when parsing a URL like `foo://`
7945
62.5k
  add_authority_slashes_if_needed();
7946
7947
62.5k
  bool has_credentials = components.protocol_end + 2 < components.host_start;
7948
62.5k
  uint32_t new_difference =
7949
62.5k
      replace_and_resize(components.host_start, components.host_end, input);
7950
7951
62.5k
  if (has_credentials) {
7952
14.6k
    buffer.insert(components.host_start, "@");
7953
14.6k
    new_difference++;
7954
14.6k
  }
7955
62.5k
  components.host_end += new_difference;
7956
62.5k
  components.pathname_start += new_difference;
7957
62.5k
  if (components.search_start != url_components::omitted) {
7958
420
    components.search_start += new_difference;
7959
420
  }
7960
62.5k
  if (components.hash_start != url_components::omitted) {
7961
315
    components.hash_start += new_difference;
7962
315
  }
7963
62.5k
  ADA_ASSERT_TRUE(validate());
7964
62.5k
}
7965
7966
[[nodiscard]] ada_really_inline uint32_t
7967
43.4k
url_aggregator::get_pathname_length() const noexcept {
7968
43.4k
  ada_log("url_aggregator::get_pathname_length");
7969
43.4k
  uint32_t ending_index = uint32_t(buffer.size());
7970
43.4k
  if (components.search_start != url_components::omitted) {
7971
393
    ending_index = components.search_start;
7972
43.0k
  } else if (components.hash_start != url_components::omitted) {
7973
212
    ending_index = components.hash_start;
7974
212
  }
7975
43.4k
  return ending_index - components.pathname_start;
7976
43.4k
}
7977
7978
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
7979
5.49k
    const noexcept {
7980
5.49k
  return buffer.size() == components.pathname_start;
7981
5.49k
}
7982
7983
435
inline void url_aggregator::update_base_search(std::string_view input) {
7984
435
  ada_log("url_aggregator::update_base_search ", input);
7985
435
  ADA_ASSERT_TRUE(validate());
7986
435
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
7987
435
  if (input.empty()) {
7988
435
    clear_search();
7989
435
    return;
7990
435
  }
7991
7992
0
  if (input[0] == '?') {
7993
0
    input.remove_prefix(1);
7994
0
  }
7995
7996
0
  if (components.hash_start == url_components::omitted) {
7997
0
    if (components.search_start == url_components::omitted) {
7998
0
      components.search_start = uint32_t(buffer.size());
7999
0
      buffer += "?";
8000
0
    } else {
8001
0
      buffer.resize(components.search_start + 1);
8002
0
    }
8003
8004
0
    buffer.append(input);
8005
0
  } else {
8006
0
    if (components.search_start == url_components::omitted) {
8007
0
      components.search_start = components.hash_start;
8008
0
    } else {
8009
0
      buffer.erase(components.search_start,
8010
0
                   components.hash_start - components.search_start);
8011
0
      components.hash_start = components.search_start;
8012
0
    }
8013
8014
0
    buffer.insert(components.search_start, "?");
8015
0
    buffer.insert(components.search_start + 1, input);
8016
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8017
0
  }
8018
8019
0
  ADA_ASSERT_TRUE(validate());
8020
0
}
8021
8022
inline void url_aggregator::update_base_search(
8023
16.8k
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8024
16.8k
  ada_log("url_aggregator::update_base_search ", input,
8025
16.8k
          " with encoding parameter ", to_string(), "\n", to_diagram());
8026
16.8k
  ADA_ASSERT_TRUE(validate());
8027
16.8k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8028
8029
16.8k
  if (components.hash_start == url_components::omitted) {
8030
16.4k
    if (components.search_start == url_components::omitted) {
8031
16.0k
      components.search_start = uint32_t(buffer.size());
8032
16.0k
      buffer += "?";
8033
16.0k
    } else {
8034
378
      buffer.resize(components.search_start + 1);
8035
378
    }
8036
8037
16.4k
    bool encoding_required =
8038
16.4k
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8039
    // When encoding_required is false, then buffer is left unchanged, and
8040
    // percent encoding was not deemed required.
8041
16.4k
    if (!encoding_required) {
8042
7.28k
      buffer.append(input);
8043
7.28k
    }
8044
16.4k
  } else {
8045
360
    if (components.search_start == url_components::omitted) {
8046
270
      components.search_start = components.hash_start;
8047
270
    } else {
8048
90
      buffer.erase(components.search_start,
8049
90
                   components.hash_start - components.search_start);
8050
90
      components.hash_start = components.search_start;
8051
90
    }
8052
8053
360
    buffer.insert(components.search_start, "?");
8054
360
    size_t idx =
8055
360
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8056
360
    if (idx == input.size()) {
8057
0
      buffer.insert(components.search_start + 1, input);
8058
0
      components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8059
360
    } else {
8060
360
      buffer.insert(components.search_start + 1, input, 0, idx);
8061
360
      input.remove_prefix(idx);
8062
      // We only create a temporary string if we need percent encoding and
8063
      // we attempt to create as small a temporary string as we can.
8064
360
      std::string encoded =
8065
360
          ada::unicode::percent_encode(input, query_percent_encode_set);
8066
360
      buffer.insert(components.search_start + idx + 1, encoded);
8067
360
      components.hash_start +=
8068
360
          uint32_t(encoded.size() + idx + 1);  // Do not forget `?`
8069
360
    }
8070
360
  }
8071
8072
16.8k
  ADA_ASSERT_TRUE(validate());
8073
16.8k
}
8074
8075
43.4k
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8076
43.4k
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8077
43.4k
          " bytes] \n", to_diagram());
8078
43.4k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8079
43.4k
  ADA_ASSERT_TRUE(validate());
8080
8081
43.4k
  const bool begins_with_dashdash = input.starts_with("//");
8082
43.4k
  if (!begins_with_dashdash && has_dash_dot()) {
8083
    // We must delete the ./
8084
0
    delete_dash_dot();
8085
0
  }
8086
8087
43.4k
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8088
140
      !has_dash_dot()) {
8089
    // If url's host is null, url does not have an opaque path, url's path's
8090
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8091
    // output.
8092
140
    buffer.insert(components.pathname_start, "/.");
8093
140
    components.pathname_start += 2;
8094
140
    if (components.search_start != url_components::omitted) {
8095
0
      components.search_start += 2;
8096
0
    }
8097
140
    if (components.hash_start != url_components::omitted) {
8098
0
      components.hash_start += 2;
8099
0
    }
8100
140
  }
8101
8102
43.4k
  uint32_t difference = replace_and_resize(
8103
43.4k
      components.pathname_start,
8104
43.4k
      components.pathname_start + get_pathname_length(), input);
8105
43.4k
  if (components.search_start != url_components::omitted) {
8106
393
    components.search_start += difference;
8107
393
  }
8108
43.4k
  if (components.hash_start != url_components::omitted) {
8109
296
    components.hash_start += difference;
8110
296
  }
8111
43.4k
  ADA_ASSERT_TRUE(validate());
8112
43.4k
}
8113
8114
0
inline void url_aggregator::append_base_pathname(const std::string_view input) {
8115
0
  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
8116
0
          "\n", to_diagram());
8117
0
  ADA_ASSERT_TRUE(validate());
8118
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8119
#if ADA_DEVELOPMENT_CHECKS
8120
  // computing the expected password.
8121
  std::string path_expected(get_pathname());
8122
  path_expected.append(input);
8123
#endif  // ADA_DEVELOPMENT_CHECKS
8124
0
  uint32_t ending_index = uint32_t(buffer.size());
8125
0
  if (components.search_start != url_components::omitted) {
8126
0
    ending_index = components.search_start;
8127
0
  } else if (components.hash_start != url_components::omitted) {
8128
0
    ending_index = components.hash_start;
8129
0
  }
8130
0
  buffer.insert(ending_index, input);
8131
8132
0
  if (components.search_start != url_components::omitted) {
8133
0
    components.search_start += uint32_t(input.size());
8134
0
  }
8135
0
  if (components.hash_start != url_components::omitted) {
8136
0
    components.hash_start += uint32_t(input.size());
8137
0
  }
8138
#if ADA_DEVELOPMENT_CHECKS
8139
  std::string path_after = std::string(get_pathname());
8140
  ADA_ASSERT_EQUAL(
8141
      path_expected, path_after,
8142
      "append_base_pathname problem after inserting " + std::string(input));
8143
#endif  // ADA_DEVELOPMENT_CHECKS
8144
0
  ADA_ASSERT_TRUE(validate());
8145
0
}
8146
8147
12.5k
inline void url_aggregator::update_base_username(const std::string_view input) {
8148
12.5k
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8149
12.5k
          "\n", to_diagram());
8150
12.5k
  ADA_ASSERT_TRUE(validate());
8151
12.5k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8152
8153
12.5k
  add_authority_slashes_if_needed();
8154
8155
12.5k
  bool has_password = has_non_empty_password();
8156
12.5k
  bool host_starts_with_at = buffer.size() > components.host_start &&
8157
12.5k
                             buffer[components.host_start] == '@';
8158
12.5k
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8159
12.5k
                                     components.username_end, input);
8160
8161
12.5k
  components.username_end += diff;
8162
12.5k
  components.host_start += diff;
8163
8164
12.5k
  if (!input.empty() && !host_starts_with_at) {
8165
12.2k
    buffer.insert(components.host_start, "@");
8166
12.2k
    diff++;
8167
12.2k
  } else if (input.empty() && host_starts_with_at && !has_password) {
8168
    // Input is empty, there is no password, and we need to remove "@" from
8169
    // hostname
8170
0
    buffer.erase(components.host_start, 1);
8171
0
    diff--;
8172
0
  }
8173
8174
12.5k
  components.host_end += diff;
8175
12.5k
  components.pathname_start += diff;
8176
12.5k
  if (components.search_start != url_components::omitted) {
8177
363
    components.search_start += diff;
8178
363
  }
8179
12.5k
  if (components.hash_start != url_components::omitted) {
8180
273
    components.hash_start += diff;
8181
273
  }
8182
12.5k
  ADA_ASSERT_TRUE(validate());
8183
12.5k
}
8184
8185
29.4k
inline void url_aggregator::append_base_username(const std::string_view input) {
8186
29.4k
  ada_log("url_aggregator::append_base_username ", input);
8187
29.4k
  ADA_ASSERT_TRUE(validate());
8188
29.4k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8189
#if ADA_DEVELOPMENT_CHECKS
8190
  // computing the expected password.
8191
  std::string username_expected(get_username());
8192
  username_expected.append(input);
8193
#endif  // ADA_DEVELOPMENT_CHECKS
8194
29.4k
  add_authority_slashes_if_needed();
8195
8196
  // If input is empty, do nothing.
8197
29.4k
  if (input.empty()) {
8198
7.76k
    return;
8199
7.76k
  }
8200
8201
21.6k
  uint32_t difference = uint32_t(input.size());
8202
21.6k
  buffer.insert(components.username_end, input);
8203
21.6k
  components.username_end += difference;
8204
21.6k
  components.host_start += difference;
8205
8206
21.6k
  if (buffer[components.host_start] != '@' &&
8207
1.39k
      components.host_start != components.host_end) {
8208
1.39k
    buffer.insert(components.host_start, "@");
8209
1.39k
    difference++;
8210
1.39k
  }
8211
8212
21.6k
  components.host_end += difference;
8213
21.6k
  components.pathname_start += difference;
8214
21.6k
  if (components.search_start != url_components::omitted) {
8215
0
    components.search_start += difference;
8216
0
  }
8217
21.6k
  if (components.hash_start != url_components::omitted) {
8218
0
    components.hash_start += difference;
8219
0
  }
8220
#if ADA_DEVELOPMENT_CHECKS
8221
  std::string username_after(get_username());
8222
  ADA_ASSERT_EQUAL(
8223
      username_expected, username_after,
8224
      "append_base_username problem after inserting " + std::string(input));
8225
#endif  // ADA_DEVELOPMENT_CHECKS
8226
21.6k
  ADA_ASSERT_TRUE(validate());
8227
21.6k
}
8228
8229
1
constexpr void url_aggregator::clear_password() {
8230
1
  ada_log("url_aggregator::clear_password ", to_string());
8231
1
  ADA_ASSERT_TRUE(validate());
8232
1
  if (!has_password()) {
8233
1
    return;
8234
1
  }
8235
8236
0
  uint32_t diff = components.host_start - components.username_end;
8237
0
  buffer.erase(components.username_end, diff);
8238
0
  components.host_start -= diff;
8239
0
  components.host_end -= diff;
8240
0
  components.pathname_start -= diff;
8241
0
  if (components.search_start != url_components::omitted) {
8242
0
    components.search_start -= diff;
8243
0
  }
8244
0
  if (components.hash_start != url_components::omitted) {
8245
0
    components.hash_start -= diff;
8246
0
  }
8247
0
}
8248
8249
12.5k
inline void url_aggregator::update_base_password(const std::string_view input) {
8250
12.5k
  ada_log("url_aggregator::update_base_password ", input);
8251
12.5k
  ADA_ASSERT_TRUE(validate());
8252
12.5k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8253
8254
12.5k
  add_authority_slashes_if_needed();
8255
8256
  // TODO: Optimization opportunity. Merge the following removal functions.
8257
12.5k
  if (input.empty()) {
8258
1
    clear_password();
8259
8260
    // Remove username too, if it is empty.
8261
1
    if (!has_non_empty_username()) {
8262
1
      update_base_username("");
8263
1
    }
8264
8265
1
    return;
8266
1
  }
8267
8268
12.5k
  bool password_exists = has_password();
8269
12.5k
  uint32_t difference = uint32_t(input.size());
8270
8271
12.5k
  if (password_exists) {
8272
135
    uint32_t current_length =
8273
135
        components.host_start - components.username_end - 1;
8274
135
    buffer.erase(components.username_end + 1, current_length);
8275
135
    difference -= current_length;
8276
12.4k
  } else {
8277
12.4k
    buffer.insert(components.username_end, ":");
8278
12.4k
    difference++;
8279
12.4k
  }
8280
8281
12.5k
  buffer.insert(components.username_end + 1, input);
8282
12.5k
  components.host_start += difference;
8283
8284
  // The following line is required to add "@" to hostname. When updating
8285
  // password if hostname does not start with "@", it is "update_base_password"s
8286
  // responsibility to set it.
8287
12.5k
  if (buffer[components.host_start] != '@') {
8288
0
    buffer.insert(components.host_start, "@");
8289
0
    difference++;
8290
0
  }
8291
8292
12.5k
  components.host_end += difference;
8293
12.5k
  components.pathname_start += difference;
8294
12.5k
  if (components.search_start != url_components::omitted) {
8295
363
    components.search_start += difference;
8296
363
  }
8297
12.5k
  if (components.hash_start != url_components::omitted) {
8298
273
    components.hash_start += difference;
8299
273
  }
8300
12.5k
  ADA_ASSERT_TRUE(validate());
8301
12.5k
}
8302
8303
25.2k
inline void url_aggregator::append_base_password(const std::string_view input) {
8304
25.2k
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8305
25.2k
          "\n", to_diagram());
8306
25.2k
  ADA_ASSERT_TRUE(validate());
8307
25.2k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8308
#if ADA_DEVELOPMENT_CHECKS
8309
  // computing the expected password.
8310
  std::string password_expected = std::string(get_password());
8311
  password_expected.append(input);
8312
#endif  // ADA_DEVELOPMENT_CHECKS
8313
25.2k
  add_authority_slashes_if_needed();
8314
8315
  // If input is empty, do nothing.
8316
25.2k
  if (input.empty()) {
8317
6.45k
    return;
8318
6.45k
  }
8319
8320
18.7k
  uint32_t difference = uint32_t(input.size());
8321
18.7k
  if (has_password()) {
8322
17.9k
    buffer.insert(components.host_start, input);
8323
17.9k
  } else {
8324
780
    difference++;  // Increment for ":"
8325
780
    buffer.insert(components.username_end, ":");
8326
780
    buffer.insert(components.username_end + 1, input);
8327
780
  }
8328
18.7k
  components.host_start += difference;
8329
8330
  // The following line is required to add "@" to hostname. When updating
8331
  // password if hostname does not start with "@", it is "append_base_password"s
8332
  // responsibility to set it.
8333
18.7k
  if (buffer[components.host_start] != '@') {
8334
527
    buffer.insert(components.host_start, "@");
8335
527
    difference++;
8336
527
  }
8337
8338
18.7k
  components.host_end += difference;
8339
18.7k
  components.pathname_start += difference;
8340
18.7k
  if (components.search_start != url_components::omitted) {
8341
0
    components.search_start += difference;
8342
0
  }
8343
18.7k
  if (components.hash_start != url_components::omitted) {
8344
0
    components.hash_start += difference;
8345
0
  }
8346
#if ADA_DEVELOPMENT_CHECKS
8347
  std::string password_after(get_password());
8348
  ADA_ASSERT_EQUAL(
8349
      password_expected, password_after,
8350
      "append_base_password problem after inserting " + std::string(input));
8351
#endif  // ADA_DEVELOPMENT_CHECKS
8352
18.7k
  ADA_ASSERT_TRUE(validate());
8353
18.7k
}
8354
8355
11.3k
inline void url_aggregator::update_base_port(uint32_t input) {
8356
11.3k
  ada_log("url_aggregator::update_base_port");
8357
11.3k
  ADA_ASSERT_TRUE(validate());
8358
11.3k
  if (input == url_components::omitted) {
8359
8.48k
    clear_port();
8360
8.48k
    return;
8361
8.48k
  }
8362
  // calling std::to_string(input.value()) is unfortunate given that the port
8363
  // value is probably already available as a string.
8364
2.85k
  std::string value = helpers::concat(":", std::to_string(input));
8365
2.85k
  uint32_t difference = uint32_t(value.size());
8366
8367
2.85k
  if (components.port != url_components::omitted) {
8368
119
    difference -= components.pathname_start - components.host_end;
8369
119
    buffer.erase(components.host_end,
8370
119
                 components.pathname_start - components.host_end);
8371
119
  }
8372
8373
2.85k
  buffer.insert(components.host_end, value);
8374
2.85k
  components.pathname_start += difference;
8375
2.85k
  if (components.search_start != url_components::omitted) {
8376
702
    components.search_start += difference;
8377
702
  }
8378
2.85k
  if (components.hash_start != url_components::omitted) {
8379
704
    components.hash_start += difference;
8380
704
  }
8381
2.85k
  components.port = input;
8382
2.85k
  ADA_ASSERT_TRUE(validate());
8383
2.85k
}
8384
8385
25.9k
inline void url_aggregator::clear_port() {
8386
25.9k
  ada_log("url_aggregator::clear_port");
8387
25.9k
  ADA_ASSERT_TRUE(validate());
8388
25.9k
  if (components.port == url_components::omitted) {
8389
23.9k
    return;
8390
23.9k
  }
8391
2.00k
  uint32_t length = components.pathname_start - components.host_end;
8392
2.00k
  buffer.erase(components.host_end, length);
8393
2.00k
  components.pathname_start -= length;
8394
2.00k
  if (components.search_start != url_components::omitted) {
8395
1.95k
    components.search_start -= length;
8396
1.95k
  }
8397
2.00k
  if (components.hash_start != url_components::omitted) {
8398
1.94k
    components.hash_start -= length;
8399
1.94k
  }
8400
2.00k
  components.port = url_components::omitted;
8401
2.00k
  ADA_ASSERT_TRUE(validate());
8402
2.00k
}
8403
8404
326
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8405
326
  ada_log("url_aggregator::retrieve_base_port");
8406
326
  return components.port;
8407
326
}
8408
8409
16.1k
inline void url_aggregator::clear_search() {
8410
16.1k
  ada_log("url_aggregator::clear_search");
8411
16.1k
  ADA_ASSERT_TRUE(validate());
8412
16.1k
  if (components.search_start == url_components::omitted) {
8413
635
    return;
8414
635
  }
8415
8416
15.5k
  if (components.hash_start == url_components::omitted) {
8417
6.55k
    buffer.resize(components.search_start);
8418
9.00k
  } else {
8419
9.00k
    buffer.erase(components.search_start,
8420
9.00k
                 components.hash_start - components.search_start);
8421
9.00k
    components.hash_start = components.search_start;
8422
9.00k
  }
8423
8424
15.5k
  components.search_start = url_components::omitted;
8425
8426
#if ADA_DEVELOPMENT_CHECKS
8427
  ADA_ASSERT_EQUAL(get_search(), "",
8428
                   "search should have been cleared on buffer=" + buffer +
8429
                       " with " + components.to_string() + "\n" + to_diagram());
8430
#endif
8431
15.5k
  ADA_ASSERT_TRUE(validate());
8432
15.5k
}
8433
8434
15.5k
inline void url_aggregator::clear_hash() {
8435
15.5k
  ada_log("url_aggregator::clear_hash");
8436
15.5k
  ADA_ASSERT_TRUE(validate());
8437
15.5k
  if (components.hash_start == url_components::omitted) {
8438
1
    return;
8439
1
  }
8440
15.5k
  buffer.resize(components.hash_start);
8441
15.5k
  components.hash_start = url_components::omitted;
8442
8443
#if ADA_DEVELOPMENT_CHECKS
8444
  ADA_ASSERT_EQUAL(get_hash(), "",
8445
                   "hash should have been cleared on buffer=" + buffer +
8446
                       " with " + components.to_string() + "\n" + to_diagram());
8447
#endif
8448
15.5k
  ADA_ASSERT_TRUE(validate());
8449
15.5k
}
8450
8451
14.3k
constexpr void url_aggregator::clear_pathname() {
8452
14.3k
  ada_log("url_aggregator::clear_pathname");
8453
14.3k
  ADA_ASSERT_TRUE(validate());
8454
14.3k
  uint32_t ending_index = uint32_t(buffer.size());
8455
14.3k
  if (components.search_start != url_components::omitted) {
8456
393
    ending_index = components.search_start;
8457
13.9k
  } else if (components.hash_start != url_components::omitted) {
8458
212
    ending_index = components.hash_start;
8459
212
  }
8460
14.3k
  uint32_t pathname_length = ending_index - components.pathname_start;
8461
14.3k
  buffer.erase(components.pathname_start, pathname_length);
8462
14.3k
  uint32_t difference = pathname_length;
8463
14.3k
  if (components.pathname_start == components.host_end + 2 &&
8464
1.01k
      buffer[components.host_end] == '/' &&
8465
56
      buffer[components.host_end + 1] == '.') {
8466
56
    components.pathname_start -= 2;
8467
56
    buffer.erase(components.host_end, 2);
8468
56
    difference += 2;
8469
56
  }
8470
14.3k
  if (components.search_start != url_components::omitted) {
8471
393
    components.search_start -= difference;
8472
393
  }
8473
14.3k
  if (components.hash_start != url_components::omitted) {
8474
296
    components.hash_start -= difference;
8475
296
  }
8476
14.3k
  ada_log("url_aggregator::clear_pathname completed, running checks...");
8477
#if ADA_DEVELOPMENT_CHECKS
8478
  ADA_ASSERT_EQUAL(get_pathname(), "",
8479
                   "pathname should have been cleared on buffer=" + buffer +
8480
                       " with " + components.to_string() + "\n" + to_diagram());
8481
#endif
8482
14.3k
  ADA_ASSERT_TRUE(validate());
8483
14.3k
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
8484
14.3k
}
8485
8486
6
constexpr void url_aggregator::clear_hostname() {
8487
6
  ada_log("url_aggregator::clear_hostname");
8488
6
  ADA_ASSERT_TRUE(validate());
8489
6
  if (!has_authority()) {
8490
0
    return;
8491
0
  }
8492
6
  ADA_ASSERT_TRUE(has_authority());
8493
8494
6
  uint32_t hostname_length = components.host_end - components.host_start;
8495
6
  uint32_t start = components.host_start;
8496
8497
  // If hostname starts with "@", we should not remove that character.
8498
6
  if (hostname_length > 0 && buffer[start] == '@') {
8499
0
    start++;
8500
0
    hostname_length--;
8501
0
  }
8502
6
  buffer.erase(start, hostname_length);
8503
6
  components.host_end = start;
8504
6
  components.pathname_start -= hostname_length;
8505
6
  if (components.search_start != url_components::omitted) {
8506
0
    components.search_start -= hostname_length;
8507
0
  }
8508
6
  if (components.hash_start != url_components::omitted) {
8509
0
    components.hash_start -= hostname_length;
8510
0
  }
8511
#if ADA_DEVELOPMENT_CHECKS
8512
  ADA_ASSERT_EQUAL(get_hostname(), "",
8513
                   "hostname should have been cleared on buffer=" + buffer +
8514
                       " with " + components.to_string() + "\n" + to_diagram());
8515
#endif
8516
6
  ADA_ASSERT_TRUE(has_authority());
8517
6
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
8518
6
                   "hostname should have been cleared on buffer=" + buffer +
8519
6
                       " with " + components.to_string() + "\n" + to_diagram());
8520
6
  ADA_ASSERT_TRUE(validate());
8521
6
}
8522
8523
6.55k
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
8524
6.55k
  ada_log("url_aggregator::has_hash");
8525
6.55k
  return components.hash_start != url_components::omitted;
8526
6.55k
}
8527
8528
6.55k
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
8529
6.55k
  ada_log("url_aggregator::has_search");
8530
6.55k
  return components.search_start != url_components::omitted;
8531
6.55k
}
8532
8533
21.3k
constexpr bool url_aggregator::has_credentials() const noexcept {
8534
21.3k
  ada_log("url_aggregator::has_credentials");
8535
21.3k
  return has_non_empty_username() || has_non_empty_password();
8536
21.3k
}
8537
8538
53.5k
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
8539
53.5k
  ada_log("url_aggregator::cannot_have_credentials_or_port");
8540
53.5k
  return type == ada::scheme::type::FILE ||
8541
48.3k
         components.host_start == components.host_end;
8542
53.5k
}
8543
8544
[[nodiscard]] ada_really_inline const ada::url_components &
8545
6.88k
url_aggregator::get_components() const noexcept {
8546
6.88k
  return components;
8547
6.88k
}
8548
8549
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
8550
163k
    const noexcept {
8551
163k
  ada_log("url_aggregator::has_authority");
8552
  // Performance: instead of doing this potentially expensive check, we could
8553
  // have a boolean in the struct.
8554
163k
  return components.protocol_end + 2 <= components.host_start &&
8555
121k
         helpers::substring(buffer, components.protocol_end,
8556
121k
                            components.protocol_end + 2) == "//";
8557
163k
}
8558
8559
142k
inline void ada::url_aggregator::add_authority_slashes_if_needed() {
8560
142k
  ada_log("url_aggregator::add_authority_slashes_if_needed");
8561
142k
  ADA_ASSERT_TRUE(validate());
8562
  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
8563
  // to insert
8564
  // `//` initially to the buffer, since it depends on the hostname existence.
8565
142k
  if (has_authority()) {
8566
104k
    return;
8567
104k
  }
8568
  // Performance: the common case is components.protocol_end == buffer.size()
8569
  // Optimization opportunity: in many cases, the "//" is part of the input and
8570
  // the insert could be fused with another insert.
8571
37.3k
  buffer.insert(components.protocol_end, "//");
8572
37.3k
  components.username_end += 2;
8573
37.3k
  components.host_start += 2;
8574
37.3k
  components.host_end += 2;
8575
37.3k
  components.pathname_start += 2;
8576
37.3k
  if (components.search_start != url_components::omitted) {
8577
155
    components.search_start += 2;
8578
155
  }
8579
37.3k
  if (components.hash_start != url_components::omitted) {
8580
97
    components.hash_start += 2;
8581
97
  }
8582
37.3k
  ADA_ASSERT_TRUE(validate());
8583
37.3k
}
8584
8585
42.0k
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
8586
42.0k
  buffer.reserve(capacity);
8587
42.0k
}
8588
8589
50.0k
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
8590
50.0k
  ada_log("url_aggregator::has_non_empty_username");
8591
50.0k
  return components.protocol_end + 2 < components.username_end;
8592
50.0k
}
8593
8594
51.5k
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
8595
51.5k
  ada_log("url_aggregator::has_non_empty_password");
8596
51.5k
  return components.host_start > components.username_end;
8597
51.5k
}
8598
8599
37.8k
constexpr bool url_aggregator::has_password() const noexcept {
8600
37.8k
  ada_log("url_aggregator::has_password");
8601
  // This function does not care about the length of the password
8602
37.8k
  return components.host_start > components.username_end &&
8603
22.3k
         buffer[components.username_end] == ':';
8604
37.8k
}
8605
8606
6.55k
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
8607
6.55k
  if (!has_hostname()) {
8608
1.24k
    return false;
8609
1.24k
  }
8610
5.31k
  if (components.host_start == components.host_end) {
8611
691
    return true;
8612
691
  }
8613
4.62k
  if (components.host_end > components.host_start + 1) {
8614
4.59k
    return false;
8615
4.59k
  }
8616
29
  return components.username_end != components.host_start;
8617
4.62k
}
8618
8619
19.7k
constexpr bool url_aggregator::has_hostname() const noexcept {
8620
19.7k
  return has_authority();
8621
19.7k
}
8622
8623
6.55k
constexpr bool url_aggregator::has_port() const noexcept {
8624
6.55k
  ada_log("url_aggregator::has_port");
8625
  // A URL cannot have a username/password/port if its host is null or the empty
8626
  // string, or its scheme is "file".
8627
6.55k
  return has_hostname() && components.pathname_start != components.host_end;
8628
6.55k
}
8629
8630
47.1k
[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
8631
  // If url's host is null, url does not have an opaque path, url's path's size
8632
  // is greater than 1, and url's path[0] is the empty string, then append
8633
  // U+002F (/) followed by U+002E (.) to output.
8634
47.1k
  ada_log("url_aggregator::has_dash_dot");
8635
#if ADA_DEVELOPMENT_CHECKS
8636
  // If pathname_start and host_end are exactly two characters apart, then we
8637
  // either have a one-digit port such as http://test.com:5?param=1 or else we
8638
  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
8639
  if (components.pathname_start == components.host_end + 2) {
8640
    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
8641
                     buffer[components.host_end + 1] == '.') ||
8642
                    (buffer[components.host_end] == ':' &&
8643
                     checkers::is_digit(buffer[components.host_end + 1])));
8644
  }
8645
  if (components.pathname_start == components.host_end + 2 &&
8646
      buffer[components.host_end] == '/' &&
8647
      buffer[components.host_end + 1] == '.') {
8648
    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
8649
    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
8650
    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
8651
  }
8652
#endif
8653
  // Performance: it should be uncommon for components.pathname_start ==
8654
  // components.host_end + 2 to be true. So we put this check first in the
8655
  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
8656
  // more expensive, but should be uncommon.
8657
47.1k
  return components.pathname_start == components.host_end + 2 &&
8658
625
         !has_opaque_path && buffer[components.host_end] == '/' &&
8659
0
         buffer[components.host_end + 1] == '.';
8660
47.1k
}
8661
8662
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
8663
16.0k
    const noexcept ada_lifetime_bound {
8664
16.0k
  ada_log("url_aggregator::get_href");
8665
16.0k
  return buffer;
8666
16.0k
}
8667
8668
ada_really_inline size_t
8669
5.75k
url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
8670
5.75k
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
8671
5.75k
  if (!view.empty() && view[0] == '-') {
8672
13
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
8673
13
    is_valid = false;
8674
13
    return 0;
8675
13
  }
8676
5.74k
  uint16_t parsed_port{};
8677
5.74k
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
8678
5.74k
  if (r.ec == std::errc::result_out_of_range) {
8679
403
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
8680
403
    is_valid = false;
8681
403
    return 0;
8682
403
  }
8683
5.34k
  ada_log("parse_port: ", parsed_port);
8684
5.34k
  const size_t consumed = size_t(r.ptr - view.data());
8685
5.34k
  ada_log("parse_port: consumed ", consumed);
8686
5.34k
  if (check_trailing_content) {
8687
3.06k
    is_valid &=
8688
3.06k
        (consumed == view.size() || view[consumed] == '/' ||
8689
664
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
8690
3.06k
  }
8691
5.34k
  ada_log("parse_port: is_valid = ", is_valid);
8692
5.34k
  if (is_valid) {
8693
4.61k
    ada_log("parse_port", r.ec == std::errc());
8694
    // scheme_default_port can return 0, and we should allow 0 as a base port.
8695
4.61k
    auto default_port = scheme_default_port();
8696
4.61k
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
8697
4.37k
                         (default_port != parsed_port);
8698
4.61k
    if (r.ec == std::errc() && is_port_valid) {
8699
2.79k
      update_base_port(parsed_port);
8700
2.79k
    } else {
8701
1.82k
      clear_port();
8702
1.82k
    }
8703
4.61k
  }
8704
5.34k
  return consumed;
8705
5.74k
}
8706
8707
5.88k
constexpr void url_aggregator::set_protocol_as_file() {
8708
5.88k
  ada_log("url_aggregator::set_protocol_as_file ");
8709
5.88k
  ADA_ASSERT_TRUE(validate());
8710
5.88k
  type = ada::scheme::type::FILE;
8711
  // next line could overflow but unsigned arithmetic has well-defined
8712
  // overflows.
8713
5.88k
  uint32_t new_difference = 5 - components.protocol_end;
8714
8715
5.88k
  if (buffer.empty()) {
8716
112
    buffer.append("file:");
8717
5.76k
  } else {
8718
5.76k
    buffer.erase(0, components.protocol_end);
8719
5.76k
    buffer.insert(0, "file:");
8720
5.76k
  }
8721
5.88k
  components.protocol_end = 5;
8722
8723
  // Update the rest of the components.
8724
5.88k
  components.username_end += new_difference;
8725
5.88k
  components.host_start += new_difference;
8726
5.88k
  components.host_end += new_difference;
8727
5.88k
  components.pathname_start += new_difference;
8728
5.88k
  if (components.search_start != url_components::omitted) {
8729
0
    components.search_start += new_difference;
8730
0
  }
8731
5.88k
  if (components.hash_start != url_components::omitted) {
8732
0
    components.hash_start += new_difference;
8733
0
  }
8734
5.88k
  ADA_ASSERT_TRUE(validate());
8735
5.88k
}
8736
8737
12.0k
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
8738
12.0k
  if (!is_valid) {
8739
2.44k
    return true;
8740
2.44k
  }
8741
9.60k
  if (!components.check_offset_consistency()) {
8742
0
    ada_log("url_aggregator::validate inconsistent components \n",
8743
0
            to_diagram());
8744
0
    return false;
8745
0
  }
8746
  // We have a credible components struct, but let us investivate more
8747
  // carefully:
8748
  /**
8749
   * https://user:pass@example.com:1234/foo/bar?baz#quux
8750
   *       |     |    |          | ^^^^|       |   |
8751
   *       |     |    |          | |   |       |   `----- hash_start
8752
   *       |     |    |          | |   |       `--------- search_start
8753
   *       |     |    |          | |   `----------------- pathname_start
8754
   *       |     |    |          | `--------------------- port
8755
   *       |     |    |          `----------------------- host_end
8756
   *       |     |    `---------------------------------- host_start
8757
   *       |     `--------------------------------------- username_end
8758
   *       `--------------------------------------------- protocol_end
8759
   */
8760
9.60k
  if (components.protocol_end == url_components::omitted) {
8761
0
    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
8762
0
    return false;
8763
0
  }
8764
9.60k
  if (components.username_end == url_components::omitted) {
8765
0
    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
8766
0
    return false;
8767
0
  }
8768
9.60k
  if (components.host_start == url_components::omitted) {
8769
0
    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
8770
0
    return false;
8771
0
  }
8772
9.60k
  if (components.host_end == url_components::omitted) {
8773
0
    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
8774
0
    return false;
8775
0
  }
8776
9.60k
  if (components.pathname_start == url_components::omitted) {
8777
0
    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
8778
0
    return false;
8779
0
  }
8780
8781
9.60k
  if (components.protocol_end > buffer.size()) {
8782
0
    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
8783
0
    return false;
8784
0
  }
8785
9.60k
  if (components.username_end > buffer.size()) {
8786
0
    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
8787
0
    return false;
8788
0
  }
8789
9.60k
  if (components.host_start > buffer.size()) {
8790
0
    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
8791
0
    return false;
8792
0
  }
8793
9.60k
  if (components.host_end > buffer.size()) {
8794
0
    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
8795
0
    return false;
8796
0
  }
8797
9.60k
  if (components.pathname_start > buffer.size()) {
8798
0
    ada_log("url_aggregator::validate pathname_start overflow \n",
8799
0
            to_diagram());
8800
0
    return false;
8801
0
  }
8802
8803
9.60k
  if (components.protocol_end > 0) {
8804
9.60k
    if (buffer[components.protocol_end - 1] != ':') {
8805
0
      ada_log(
8806
0
          "url_aggregator::validate missing : at the end of the protocol \n",
8807
0
          to_diagram());
8808
0
      return false;
8809
0
    }
8810
9.60k
  }
8811
8812
9.60k
  if (components.username_end != buffer.size() &&
8813
9.43k
      components.username_end > components.protocol_end + 2) {
8814
6.75k
    if (buffer[components.username_end] != ':' &&
8815
153
        buffer[components.username_end] != '@') {
8816
0
      ada_log(
8817
0
          "url_aggregator::validate missing : or @ at the end of the username "
8818
0
          "\n",
8819
0
          to_diagram());
8820
0
      return false;
8821
0
    }
8822
6.75k
  }
8823
8824
9.60k
  if (components.host_start != buffer.size()) {
8825
9.43k
    if (components.host_start > components.username_end) {
8826
6.66k
      if (buffer[components.host_start] != '@') {
8827
0
        ada_log(
8828
0
            "url_aggregator::validate missing @ at the end of the password \n",
8829
0
            to_diagram());
8830
0
        return false;
8831
0
      }
8832
6.66k
    } else if (components.host_start == components.username_end &&
8833
2.76k
               components.host_end > components.host_start) {
8834
1.26k
      if (components.host_start == components.protocol_end + 2) {
8835
1.10k
        if (buffer[components.protocol_end] != '/' ||
8836
1.10k
            buffer[components.protocol_end + 1] != '/') {
8837
0
          ada_log(
8838
0
              "url_aggregator::validate missing // between protocol and host "
8839
0
              "\n",
8840
0
              to_diagram());
8841
0
          return false;
8842
0
        }
8843
1.10k
      } else {
8844
153
        if (components.host_start > components.protocol_end &&
8845
153
            buffer[components.host_start] != '@') {
8846
0
          ada_log(
8847
0
              "url_aggregator::validate missing @ at the end of the username "
8848
0
              "\n",
8849
0
              to_diagram());
8850
0
          return false;
8851
0
        }
8852
153
      }
8853
1.50k
    } else {
8854
1.50k
      if (components.host_end != components.host_start) {
8855
0
        ada_log("url_aggregator::validate expected omitted host \n",
8856
0
                to_diagram());
8857
0
        return false;
8858
0
      }
8859
1.50k
    }
8860
9.43k
  }
8861
9.60k
  if (components.host_end != buffer.size() &&
8862
9.35k
      components.pathname_start > components.host_end) {
8863
1.12k
    if (components.pathname_start == components.host_end + 2 &&
8864
839
        buffer[components.host_end] == '/' &&
8865
13
        buffer[components.host_end + 1] == '.') {
8866
13
      if (components.pathname_start + 1 >= buffer.size() ||
8867
13
          buffer[components.pathname_start] != '/' ||
8868
13
          buffer[components.pathname_start + 1] != '/') {
8869
0
        ada_log(
8870
0
            "url_aggregator::validate expected the path to begin with // \n",
8871
0
            to_diagram());
8872
0
        return false;
8873
0
      }
8874
1.10k
    } else if (buffer[components.host_end] != ':') {
8875
0
      ada_log("url_aggregator::validate missing : at the port \n",
8876
0
              to_diagram());
8877
0
      return false;
8878
0
    }
8879
1.12k
  }
8880
9.60k
  if (components.pathname_start != buffer.size() &&
8881
9.35k
      components.pathname_start < components.search_start &&
8882
9.31k
      components.pathname_start < components.hash_start && !has_opaque_path) {
8883
8.66k
    if (buffer[components.pathname_start] != '/') {
8884
0
      ada_log("url_aggregator::validate missing / at the path \n",
8885
0
              to_diagram());
8886
0
      return false;
8887
0
    }
8888
8.66k
  }
8889
9.60k
  if (components.search_start != url_components::omitted) {
8890
6.72k
    if (buffer[components.search_start] != '?') {
8891
0
      ada_log("url_aggregator::validate missing ? at the search \n",
8892
0
              to_diagram());
8893
0
      return false;
8894
0
    }
8895
6.72k
  }
8896
9.60k
  if (components.hash_start != url_components::omitted) {
8897
6.67k
    if (buffer[components.hash_start] != '#') {
8898
0
      ada_log("url_aggregator::validate missing # at the hash \n",
8899
0
              to_diagram());
8900
0
      return false;
8901
0
    }
8902
6.67k
  }
8903
8904
9.60k
  return true;
8905
9.60k
}
8906
8907
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() const
8908
54.0k
    ada_lifetime_bound {
8909
54.0k
  ada_log("url_aggregator::get_pathname pathname_start = ",
8910
54.0k
          components.pathname_start, " buffer.size() = ", buffer.size(),
8911
54.0k
          " components.search_start = ", components.search_start,
8912
54.0k
          " components.hash_start = ", components.hash_start);
8913
54.0k
  auto ending_index = uint32_t(buffer.size());
8914
54.0k
  if (components.search_start != url_components::omitted) {
8915
23.8k
    ending_index = components.search_start;
8916
30.2k
  } else if (components.hash_start != url_components::omitted) {
8917
439
    ending_index = components.hash_start;
8918
439
  }
8919
54.0k
  return helpers::substring(buffer, components.pathname_start, ending_index);
8920
54.0k
}
8921
8922
inline std::ostream &operator<<(std::ostream &out,
8923
0
                                const ada::url_aggregator &u) {
8924
0
  return out << u.to_string();
8925
0
}
8926
8927
445
void url_aggregator::update_host_to_base_host(const std::string_view input) {
8928
445
  ada_log("url_aggregator::update_host_to_base_host ", input);
8929
445
  ADA_ASSERT_TRUE(validate());
8930
445
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8931
445
  if (type != ada::scheme::type::FILE) {
8932
    // Let host be the result of host parsing host_view with url is not special.
8933
326
    if (input.empty() && !is_special()) {
8934
129
      if (has_hostname()) {
8935
6
        clear_hostname();
8936
123
      } else if (has_dash_dot()) {
8937
0
        add_authority_slashes_if_needed();
8938
0
        delete_dash_dot();
8939
0
      }
8940
129
      return;
8941
129
    }
8942
326
  }
8943
316
  update_base_hostname(input);
8944
316
  ADA_ASSERT_TRUE(validate());
8945
316
  return;
8946
445
}
8947
}  // namespace ada
8948
8949
#endif  // ADA_URL_AGGREGATOR_INL_H
8950
/* end file include/ada/url_aggregator-inl.h */
8951
/* begin file include/ada/url_search_params.h */
8952
/**
8953
 * @file url_search_params.h
8954
 * @brief URL query string parameter manipulation.
8955
 *
8956
 * This file provides the `url_search_params` class for parsing, manipulating,
8957
 * and serializing URL query strings. It implements the URLSearchParams API
8958
 * from the WHATWG URL Standard.
8959
 *
8960
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
8961
 */
8962
#ifndef ADA_URL_SEARCH_PARAMS_H
8963
#define ADA_URL_SEARCH_PARAMS_H
8964
8965
#include <optional>
8966
#include <string>
8967
#include <string_view>
8968
#include <vector>
8969
8970
namespace ada {
8971
8972
/**
8973
 * @brief Iterator types for url_search_params iteration.
8974
 */
8975
enum class url_search_params_iter_type {
8976
  KEYS,    /**< Iterate over parameter keys only */
8977
  VALUES,  /**< Iterate over parameter values only */
8978
  ENTRIES, /**< Iterate over key-value pairs */
8979
};
8980
8981
template <typename T, url_search_params_iter_type Type>
8982
struct url_search_params_iter;
8983
8984
/** Type alias for a key-value pair of string views. */
8985
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
8986
8987
/** Iterator over search parameter keys. */
8988
using url_search_params_keys_iter =
8989
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
8990
/** Iterator over search parameter values. */
8991
using url_search_params_values_iter =
8992
    url_search_params_iter<std::string_view,
8993
                           url_search_params_iter_type::VALUES>;
8994
/** Iterator over search parameter key-value pairs. */
8995
using url_search_params_entries_iter =
8996
    url_search_params_iter<key_value_view_pair,
8997
                           url_search_params_iter_type::ENTRIES>;
8998
8999
/**
9000
 * @brief Class for parsing and manipulating URL query strings.
9001
 *
9002
 * The `url_search_params` class provides methods to parse, modify, and
9003
 * serialize URL query parameters (the part after '?' in a URL). It handles
9004
 * percent-encoding and decoding automatically.
9005
 *
9006
 * All string inputs must be valid UTF-8. The caller is responsible for
9007
 * ensuring UTF-8 validity.
9008
 *
9009
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9010
 */
9011
struct url_search_params {
9012
1.14k
  url_search_params() = default;
9013
9014
  /**
9015
   * Constructs url_search_params by parsing a query string.
9016
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9017
   */
9018
1.14k
  explicit url_search_params(const std::string_view input) {
9019
1.14k
    initialize(input);
9020
1.14k
  }
9021
9022
  url_search_params(const url_search_params &u) = default;
9023
0
  url_search_params(url_search_params &&u) noexcept = default;
9024
  url_search_params &operator=(url_search_params &&u) noexcept = default;
9025
  url_search_params &operator=(const url_search_params &u) = default;
9026
2.28k
  ~url_search_params() = default;
9027
9028
  /**
9029
   * Returns the number of key-value pairs.
9030
   * @return The total count of parameters.
9031
   */
9032
  [[nodiscard]] inline size_t size() const noexcept;
9033
9034
  /**
9035
   * Appends a new key-value pair to the parameter list.
9036
   * @param key The parameter name (must be valid UTF-8).
9037
   * @param value The parameter value (must be valid UTF-8).
9038
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9039
   */
9040
  inline void append(std::string_view key, std::string_view value);
9041
9042
  /**
9043
   * Removes all pairs with the given key.
9044
   * @param key The parameter name to remove.
9045
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9046
   */
9047
  inline void remove(std::string_view key);
9048
9049
  /**
9050
   * Removes all pairs with the given key and value.
9051
   * @param key The parameter name.
9052
   * @param value The parameter value to match.
9053
   */
9054
  inline void remove(std::string_view key, std::string_view value);
9055
9056
  /**
9057
   * Returns the value of the first pair with the given key.
9058
   * @param key The parameter name to search for.
9059
   * @return The value if found, or std::nullopt if not present.
9060
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9061
   */
9062
  inline std::optional<std::string_view> get(std::string_view key);
9063
9064
  /**
9065
   * Returns all values for pairs with the given key.
9066
   * @param key The parameter name to search for.
9067
   * @return A vector of all matching values (may be empty).
9068
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9069
   */
9070
  inline std::vector<std::string> get_all(std::string_view key);
9071
9072
  /**
9073
   * Checks if any pair has the given key.
9074
   * @param key The parameter name to search for.
9075
   * @return `true` if at least one pair has this key.
9076
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9077
   */
9078
  inline bool has(std::string_view key) noexcept;
9079
9080
  /**
9081
   * Checks if any pair matches the given key and value.
9082
   * @param key The parameter name to search for.
9083
   * @param value The parameter value to match.
9084
   * @return `true` if a matching pair exists.
9085
   */
9086
  inline bool has(std::string_view key, std::string_view value) noexcept;
9087
9088
  /**
9089
   * Sets a parameter value, replacing any existing pairs with the same key.
9090
   * @param key The parameter name (must be valid UTF-8).
9091
   * @param value The parameter value (must be valid UTF-8).
9092
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9093
   */
9094
  inline void set(std::string_view key, std::string_view value);
9095
9096
  /**
9097
   * Sorts all key-value pairs by their keys using code unit comparison.
9098
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9099
   */
9100
  inline void sort();
9101
9102
  /**
9103
   * Serializes the parameters to a query string (without leading '?').
9104
   * @return The percent-encoded query string.
9105
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9106
   */
9107
  inline std::string to_string() const;
9108
9109
  /**
9110
   * Returns an iterator over all parameter keys.
9111
   * Keys may repeat if there are duplicate parameters.
9112
   * @return An iterator yielding string_view keys.
9113
   * @note The iterator is invalidated if this object is modified.
9114
   */
9115
  inline url_search_params_keys_iter get_keys();
9116
9117
  /**
9118
   * Returns an iterator over all parameter values.
9119
   * @return An iterator yielding string_view values.
9120
   * @note The iterator is invalidated if this object is modified.
9121
   */
9122
  inline url_search_params_values_iter get_values();
9123
9124
  /**
9125
   * Returns an iterator over all key-value pairs.
9126
   * @return An iterator yielding key-value pair views.
9127
   * @note The iterator is invalidated if this object is modified.
9128
   */
9129
  inline url_search_params_entries_iter get_entries();
9130
9131
  /**
9132
   * C++ style conventional iterator support. const only because we
9133
   * do not really want the params to be modified via the iterator.
9134
   */
9135
0
  inline auto begin() const { return params.begin(); }
9136
0
  inline auto end() const { return params.end(); }
9137
0
  inline auto front() const { return params.front(); }
9138
0
  inline auto back() const { return params.back(); }
9139
0
  inline auto operator[](size_t index) const { return params[index]; }
9140
9141
  /**
9142
   * @private
9143
   * Used to reset the search params to a new input.
9144
   * Used primarily for C API.
9145
   * @param input
9146
   */
9147
  void reset(std::string_view input);
9148
9149
 private:
9150
  typedef std::pair<std::string, std::string> key_value_pair;
9151
  std::vector<key_value_pair> params{};
9152
9153
  /**
9154
   * The init parameter must be valid UTF-8.
9155
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9156
   */
9157
  void initialize(std::string_view init);
9158
9159
  template <typename T, url_search_params_iter_type Type>
9160
  friend struct url_search_params_iter;
9161
};  // url_search_params
9162
9163
/**
9164
 * @brief JavaScript-style iterator for url_search_params.
9165
 *
9166
 * Provides a `next()` method that returns successive values until exhausted.
9167
 * This matches the iterator pattern used in the Web Platform.
9168
 *
9169
 * @tparam T The type of value returned by the iterator.
9170
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9171
 *
9172
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9173
 */
9174
template <typename T, url_search_params_iter_type Type>
9175
struct url_search_params_iter {
9176
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()
9177
  url_search_params_iter(const url_search_params_iter &u) = default;
9178
  url_search_params_iter(url_search_params_iter &&u) noexcept = default;
9179
  url_search_params_iter &operator=(url_search_params_iter &&u) noexcept =
9180
      default;
9181
  url_search_params_iter &operator=(const url_search_params_iter &u) = default;
9182
  ~url_search_params_iter() = default;
9183
9184
  /**
9185
   * Returns the next value in the iteration sequence.
9186
   * @return The next value, or std::nullopt if iteration is complete.
9187
   */
9188
  inline std::optional<T> next();
9189
9190
  /**
9191
   * Checks if more values are available.
9192
   * @return `true` if `next()` will return a value, `false` if exhausted.
9193
   */
9194
  inline bool has_next() const;
9195
9196
 private:
9197
  static url_search_params EMPTY;
9198
3.42k
  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
9198
1.14k
  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
9198
1.14k
  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
9198
1.14k
  inline url_search_params_iter(url_search_params &params_) : params(params_) {}
9199
9200
  url_search_params &params;
9201
  size_t pos = 0;
9202
9203
  friend struct url_search_params;
9204
};
9205
9206
}  // namespace ada
9207
#endif
9208
/* end file include/ada/url_search_params.h */
9209
/* begin file include/ada/url_search_params-inl.h */
9210
/**
9211
 * @file url_search_params-inl.h
9212
 * @brief Inline declarations for the URL Search Params
9213
 */
9214
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9215
#define ADA_URL_SEARCH_PARAMS_INL_H
9216
9217
9218
#include <algorithm>
9219
#include <optional>
9220
#include <ranges>
9221
#include <string>
9222
#include <string_view>
9223
#include <vector>
9224
9225
namespace ada {
9226
9227
// A default, empty url_search_params for use with empty iterators.
9228
template <typename T, ada::url_search_params_iter_type Type>
9229
url_search_params url_search_params_iter<T, Type>::EMPTY;
9230
9231
1.14k
inline void url_search_params::reset(std::string_view input) {
9232
1.14k
  params.clear();
9233
1.14k
  initialize(input);
9234
1.14k
}
9235
9236
2.28k
inline void url_search_params::initialize(std::string_view input) {
9237
2.28k
  if (!input.empty() && input.front() == '?') {
9238
7
    input.remove_prefix(1);
9239
7
  }
9240
9241
5.07k
  auto process_key_value = [&](const std::string_view current) {
9242
5.07k
    auto equal = current.find('=');
9243
9244
5.07k
    if (equal == std::string_view::npos) {
9245
2.48k
      std::string name(current);
9246
2.48k
      std::ranges::replace(name, '+', ' ');
9247
2.48k
      params.emplace_back(unicode::percent_decode(name, name.find('%')), "");
9248
2.59k
    } else {
9249
2.59k
      std::string name(current.substr(0, equal));
9250
2.59k
      std::string value(current.substr(equal + 1));
9251
9252
2.59k
      std::ranges::replace(name, '+', ' ');
9253
2.59k
      std::ranges::replace(value, '+', ' ');
9254
9255
2.59k
      params.emplace_back(unicode::percent_decode(name, name.find('%')),
9256
2.59k
                          unicode::percent_decode(value, value.find('%')));
9257
2.59k
    }
9258
5.07k
  };
9259
9260
7.28k
  while (!input.empty()) {
9261
5.96k
    auto ampersand_index = input.find('&');
9262
9263
5.96k
    if (ampersand_index == std::string_view::npos) {
9264
958
      if (!input.empty()) {
9265
958
        process_key_value(input);
9266
958
      }
9267
958
      break;
9268
5.00k
    } else if (ampersand_index != 0) {
9269
4.11k
      process_key_value(input.substr(0, ampersand_index));
9270
4.11k
    }
9271
9272
5.00k
    input.remove_prefix(ampersand_index + 1);
9273
5.00k
  }
9274
2.28k
}
9275
9276
inline void url_search_params::append(const std::string_view key,
9277
2.09k
                                      const std::string_view value) {
9278
2.09k
  params.emplace_back(key, value);
9279
2.09k
}
9280
9281
0
inline size_t url_search_params::size() const noexcept { return params.size(); }
9282
9283
inline std::optional<std::string_view> url_search_params::get(
9284
0
    const std::string_view key) {
9285
0
  auto entry = std::ranges::find_if(
9286
0
      params, [&key](const auto &param) { return param.first == key; });
9287
9288
0
  if (entry == params.end()) {
9289
0
    return std::nullopt;
9290
0
  }
9291
9292
0
  return entry->second;
9293
0
}
9294
9295
inline std::vector<std::string> url_search_params::get_all(
9296
0
    const std::string_view key) {
9297
0
  std::vector<std::string> out{};
9298
9299
0
  for (auto &param : params) {
9300
0
    if (param.first == key) {
9301
0
      out.emplace_back(param.second);
9302
0
    }
9303
0
  }
9304
9305
0
  return out;
9306
0
}
9307
9308
1.14k
inline bool url_search_params::has(const std::string_view key) noexcept {
9309
1.14k
  auto entry = std::ranges::find_if(
9310
1.14k
      params, [&key](const auto &param) { return param.first == key; });
9311
1.14k
  return entry != params.end();
9312
1.14k
}
9313
9314
inline bool url_search_params::has(std::string_view key,
9315
1.14k
                                   std::string_view value) noexcept {
9316
1.14k
  auto entry = std::ranges::find_if(params, [&key, &value](const auto &param) {
9317
952
    return param.first == key && param.second == value;
9318
952
  });
9319
1.14k
  return entry != params.end();
9320
1.14k
}
9321
9322
1.14k
inline std::string url_search_params::to_string() const {
9323
1.14k
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9324
1.14k
  std::string out{};
9325
2.28k
  for (size_t i = 0; i < params.size(); i++) {
9326
1.14k
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9327
1.14k
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9328
9329
    // Performance optimization: Move this inside percent_encode.
9330
1.14k
    std::ranges::replace(key, ' ', '+');
9331
1.14k
    std::ranges::replace(value, ' ', '+');
9332
9333
1.14k
    if (i != 0) {
9334
0
      out += "&";
9335
0
    }
9336
1.14k
    out.append(key);
9337
1.14k
    out += "=";
9338
1.14k
    out.append(value);
9339
1.14k
  }
9340
1.14k
  return out;
9341
1.14k
}
9342
9343
inline void url_search_params::set(const std::string_view key,
9344
1.14k
                                   const std::string_view value) {
9345
1.14k
  const auto find = [&key](const auto &param) { return param.first == key; };
9346
9347
1.14k
  auto it = std::ranges::find_if(params, find);
9348
9349
1.14k
  if (it == params.end()) {
9350
0
    params.emplace_back(key, value);
9351
1.14k
  } else {
9352
1.14k
    it->second = value;
9353
1.14k
    params.erase(std::remove_if(std::next(it), params.end(), find),
9354
1.14k
                 params.end());
9355
1.14k
  }
9356
1.14k
}
9357
9358
2.09k
inline void url_search_params::remove(const std::string_view key) {
9359
2.09k
  std::erase_if(params,
9360
3.04k
                [&key](const auto &param) { return param.first == key; });
9361
2.09k
}
9362
9363
inline void url_search_params::remove(const std::string_view key,
9364
2.09k
                                      const std::string_view value) {
9365
2.09k
  std::erase_if(params, [&key, &value](const auto &param) {
9366
952
    return param.first == key && param.second == value;
9367
952
  });
9368
2.09k
}
9369
9370
0
inline void url_search_params::sort() {
9371
  // We rely on the fact that the content is valid UTF-8.
9372
0
  std::ranges::stable_sort(params, [](const key_value_pair &lhs,
9373
0
                                      const key_value_pair &rhs) {
9374
0
    size_t i = 0, j = 0;
9375
0
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9376
0
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9377
0
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9378
0
      uint32_t codePoint1 = 0, codePoint2 = 0;
9379
9380
0
      if (low_surrogate1 != 0) {
9381
0
        codePoint1 = low_surrogate1;
9382
0
        low_surrogate1 = 0;
9383
0
      } else {
9384
0
        uint8_t c1 = uint8_t(lhs.first[i]);
9385
0
        if (c1 <= 0x7F) {
9386
0
          codePoint1 = c1;
9387
0
          i++;
9388
0
        } else if (c1 <= 0xDF) {
9389
0
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9390
0
          i += 2;
9391
0
        } else if (c1 <= 0xEF) {
9392
0
          codePoint1 = ((c1 & 0x0F) << 12) |
9393
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9394
0
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9395
0
          i += 3;
9396
0
        } else {
9397
0
          codePoint1 = ((c1 & 0x07) << 18) |
9398
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9399
0
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9400
0
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9401
0
          i += 4;
9402
9403
0
          codePoint1 -= 0x10000;
9404
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9405
0
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9406
0
          codePoint1 = high_surrogate;
9407
0
        }
9408
0
      }
9409
9410
0
      if (low_surrogate2 != 0) {
9411
0
        codePoint2 = low_surrogate2;
9412
0
        low_surrogate2 = 0;
9413
0
      } else {
9414
0
        uint8_t c2 = uint8_t(rhs.first[j]);
9415
0
        if (c2 <= 0x7F) {
9416
0
          codePoint2 = c2;
9417
0
          j++;
9418
0
        } else if (c2 <= 0xDF) {
9419
0
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
9420
0
          j += 2;
9421
0
        } else if (c2 <= 0xEF) {
9422
0
          codePoint2 = ((c2 & 0x0F) << 12) |
9423
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
9424
0
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
9425
0
          j += 3;
9426
0
        } else {
9427
0
          codePoint2 = ((c2 & 0x07) << 18) |
9428
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
9429
0
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
9430
0
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
9431
0
          j += 4;
9432
0
          codePoint2 -= 0x10000;
9433
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
9434
0
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
9435
0
          codePoint2 = high_surrogate;
9436
0
        }
9437
0
      }
9438
9439
0
      if (codePoint1 != codePoint2) {
9440
0
        return (codePoint1 < codePoint2);
9441
0
      }
9442
0
    }
9443
0
    return (j < rhs.first.size() || low_surrogate2 != 0);
9444
0
  });
9445
0
}
9446
9447
1.14k
inline url_search_params_keys_iter url_search_params::get_keys() {
9448
1.14k
  return url_search_params_keys_iter(*this);
9449
1.14k
}
9450
9451
/**
9452
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9453
 */
9454
1.14k
inline url_search_params_values_iter url_search_params::get_values() {
9455
1.14k
  return url_search_params_values_iter(*this);
9456
1.14k
}
9457
9458
/**
9459
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9460
 */
9461
1.14k
inline url_search_params_entries_iter url_search_params::get_entries() {
9462
1.14k
  return url_search_params_entries_iter(*this);
9463
1.14k
}
9464
9465
template <typename T, url_search_params_iter_type Type>
9466
3.42k
inline bool url_search_params_iter<T, Type>::has_next() const {
9467
3.42k
  return pos < params.params.size();
9468
3.42k
}
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
9466
1.14k
inline bool url_search_params_iter<T, Type>::has_next() const {
9467
1.14k
  return pos < params.params.size();
9468
1.14k
}
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
9466
1.14k
inline bool url_search_params_iter<T, Type>::has_next() const {
9467
1.14k
  return pos < params.params.size();
9468
1.14k
}
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
9466
1.14k
inline bool url_search_params_iter<T, Type>::has_next() const {
9467
1.14k
  return pos < params.params.size();
9468
1.14k
}
9469
9470
template <>
9471
0
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
9472
0
  if (!has_next()) {
9473
0
    return std::nullopt;
9474
0
  }
9475
0
  return params.params[pos++].first;
9476
0
}
9477
9478
template <>
9479
0
inline std::optional<std::string_view> url_search_params_values_iter::next() {
9480
0
  if (!has_next()) {
9481
0
    return std::nullopt;
9482
0
  }
9483
0
  return params.params[pos++].second;
9484
0
}
9485
9486
template <>
9487
inline std::optional<key_value_view_pair>
9488
0
url_search_params_entries_iter::next() {
9489
0
  if (!has_next()) {
9490
0
    return std::nullopt;
9491
0
  }
9492
0
  return params.params[pos++];
9493
0
}
9494
9495
}  // namespace ada
9496
9497
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
9498
/* end file include/ada/url_search_params-inl.h */
9499
9500
/* begin file include/ada/url_pattern-inl.h */
9501
/**
9502
 * @file url_pattern-inl.h
9503
 * @brief Declaration for the URLPattern inline functions.
9504
 */
9505
#ifndef ADA_URL_PATTERN_INL_H
9506
#define ADA_URL_PATTERN_INL_H
9507
9508
9509
#include <algorithm>
9510
#include <string_view>
9511
#include <utility>
9512
9513
#if ADA_INCLUDE_URL_PATTERN
9514
namespace ada {
9515
9516
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
9517
0
  return protocol == other.protocol && username == other.username &&
9518
0
         password == other.password && hostname == other.hostname &&
9519
0
         port == other.port && search == other.search && hash == other.hash &&
9520
0
         pathname == other.pathname;
9521
0
}
9522
9523
inline bool url_pattern_component_result::operator==(
9524
0
    const url_pattern_component_result& other) const {
9525
0
  return input == other.input && groups == other.groups;
9526
0
}
9527
9528
template <url_pattern_regex::regex_concept regex_provider>
9529
url_pattern_component_result
9530
url_pattern_component<regex_provider>::create_component_match_result(
9531
    std::string&& input,
9532
    std::vector<std::optional<std::string>>&& exec_result) {
9533
  // Let result be a new URLPatternComponentResult.
9534
  // Set result["input"] to input.
9535
  // Let groups be a record<USVString, (USVString or undefined)>.
9536
  auto result =
9537
      url_pattern_component_result{.input = std::move(input), .groups = {}};
9538
9539
  // We explicitly start iterating from 0 even though the spec
9540
  // says we should start from 1. This case is handled by the
9541
  // std_regex_provider which removes the full match from index 0.
9542
  // Use min() to guard against potential mismatches between
9543
  // exec_result size and group_name_list size.
9544
  const size_t size = std::min(exec_result.size(), group_name_list.size());
9545
  result.groups.reserve(size);
9546
  for (size_t index = 0; index < size; index++) {
9547
    result.groups.emplace(group_name_list[index],
9548
                          std::move(exec_result[index]));
9549
  }
9550
  return result;
9551
}
9552
9553
template <url_pattern_regex::regex_concept regex_provider>
9554
std::string_view url_pattern<regex_provider>::get_protocol() const
9555
4.21k
    ada_lifetime_bound {
9556
  // Return this's associated URL pattern's protocol component's pattern string.
9557
4.21k
  return protocol_component.pattern;
9558
4.21k
}
9559
template <url_pattern_regex::regex_concept regex_provider>
9560
std::string_view url_pattern<regex_provider>::get_username() const
9561
4.21k
    ada_lifetime_bound {
9562
  // Return this's associated URL pattern's username component's pattern string.
9563
4.21k
  return username_component.pattern;
9564
4.21k
}
9565
template <url_pattern_regex::regex_concept regex_provider>
9566
std::string_view url_pattern<regex_provider>::get_password() const
9567
4.21k
    ada_lifetime_bound {
9568
  // Return this's associated URL pattern's password component's pattern string.
9569
4.21k
  return password_component.pattern;
9570
4.21k
}
9571
template <url_pattern_regex::regex_concept regex_provider>
9572
std::string_view url_pattern<regex_provider>::get_hostname() const
9573
4.21k
    ada_lifetime_bound {
9574
  // Return this's associated URL pattern's hostname component's pattern string.
9575
4.21k
  return hostname_component.pattern;
9576
4.21k
}
9577
template <url_pattern_regex::regex_concept regex_provider>
9578
std::string_view url_pattern<regex_provider>::get_port() const
9579
4.21k
    ada_lifetime_bound {
9580
  // Return this's associated URL pattern's port component's pattern string.
9581
4.21k
  return port_component.pattern;
9582
4.21k
}
9583
template <url_pattern_regex::regex_concept regex_provider>
9584
std::string_view url_pattern<regex_provider>::get_pathname() const
9585
4.21k
    ada_lifetime_bound {
9586
  // Return this's associated URL pattern's pathname component's pattern string.
9587
4.21k
  return pathname_component.pattern;
9588
4.21k
}
9589
template <url_pattern_regex::regex_concept regex_provider>
9590
std::string_view url_pattern<regex_provider>::get_search() const
9591
4.21k
    ada_lifetime_bound {
9592
  // Return this's associated URL pattern's search component's pattern string.
9593
4.21k
  return search_component.pattern;
9594
4.21k
}
9595
template <url_pattern_regex::regex_concept regex_provider>
9596
std::string_view url_pattern<regex_provider>::get_hash() const
9597
4.21k
    ada_lifetime_bound {
9598
  // Return this's associated URL pattern's hash component's pattern string.
9599
4.21k
  return hash_component.pattern;
9600
4.21k
}
9601
template <url_pattern_regex::regex_concept regex_provider>
9602
4.21k
bool url_pattern<regex_provider>::ignore_case() const {
9603
4.21k
  return ignore_case_;
9604
4.21k
}
9605
template <url_pattern_regex::regex_concept regex_provider>
9606
4.21k
bool url_pattern<regex_provider>::has_regexp_groups() const {
9607
  // If this's associated URL pattern's has regexp groups, then return true.
9608
4.21k
  return protocol_component.has_regexp_groups ||
9609
4.21k
         username_component.has_regexp_groups ||
9610
4.21k
         password_component.has_regexp_groups ||
9611
4.21k
         hostname_component.has_regexp_groups ||
9612
4.21k
         port_component.has_regexp_groups ||
9613
4.21k
         pathname_component.has_regexp_groups ||
9614
4.21k
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
9615
4.21k
}
9616
9617
33.7k
inline bool url_pattern_part::is_regexp() const noexcept {
9618
33.7k
  return type == url_pattern_part_type::REGEXP;
9619
33.7k
}
9620
9621
inline std::string_view url_pattern_compile_component_options::get_delimiter()
9622
39.5k
    const {
9623
39.5k
  if (delimiter) {
9624
8.43k
    return {&delimiter.value(), 1};
9625
8.43k
  }
9626
31.1k
  return {};
9627
39.5k
}
9628
9629
inline std::string_view url_pattern_compile_component_options::get_prefix()
9630
8.93k
    const {
9631
8.93k
  if (prefix) {
9632
8.43k
    return {&prefix.value(), 1};
9633
8.43k
  }
9634
501
  return {};
9635
8.93k
}
9636
9637
template <url_pattern_regex::regex_concept regex_provider>
9638
template <url_pattern_encoding_callback F>
9639
tl::expected<url_pattern_component<regex_provider>, errors>
9640
url_pattern_component<regex_provider>::compile(
9641
    std::string_view input, F& encoding_callback,
9642
39.5k
    url_pattern_compile_component_options& options) {
9643
39.5k
  ada_log("url_pattern_component::compile input: ", input);
9644
  // Let part list be the result of running parse a pattern string given input,
9645
  // options, and encoding callback.
9646
39.5k
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
9647
39.5k
                                                             encoding_callback);
9648
9649
39.5k
  if (!part_list) {
9650
1.65k
    ada_log("parse_pattern_string failed");
9651
1.65k
    return tl::unexpected(part_list.error());
9652
1.65k
  }
9653
9654
  // Detect pattern type early to potentially skip expensive regex compilation
9655
37.9k
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
9656
37.9k
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
9657
9658
37.9k
  url_pattern_component_type component_type =
9659
37.9k
      url_pattern_component_type::REGEXP;
9660
37.9k
  std::string exact_match_value{};
9661
9662
37.9k
  if (part_list->empty()) {
9663
4.21k
    component_type = url_pattern_component_type::EMPTY;
9664
33.7k
  } else if (part_list->size() == 1) {
9665
33.7k
    const auto& part = (*part_list)[0];
9666
33.7k
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
9667
12.6k
        part.modifier == url_pattern_part_modifier::none &&
9668
12.6k
        !options.ignore_case) {
9669
12.6k
      component_type = url_pattern_component_type::EXACT_MATCH;
9670
12.6k
      exact_match_value = part.value;
9671
21.0k
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
9672
21.0k
               part.modifier == url_pattern_part_modifier::none &&
9673
21.0k
               part.prefix.empty() && part.suffix.empty()) {
9674
16.8k
      component_type = url_pattern_component_type::FULL_WILDCARD;
9675
16.8k
    }
9676
33.7k
  }
9677
9678
  // For simple patterns, skip regex generation and compilation entirely
9679
37.9k
  if (component_type != url_pattern_component_type::REGEXP) {
9680
33.7k
    auto pattern_string =
9681
33.7k
        url_pattern_helpers::generate_pattern_string(*part_list, options);
9682
    // For FULL_WILDCARD, we need the group name from
9683
    // generate_regular_expression
9684
33.7k
    std::vector<std::string> name_list;
9685
33.7k
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
9686
16.8k
        !part_list->empty()) {
9687
16.8k
      name_list.push_back((*part_list)[0].name);
9688
16.8k
    }
9689
33.7k
    return url_pattern_component<regex_provider>(
9690
33.7k
        std::move(pattern_string), typename regex_provider::regex_type{},
9691
33.7k
        std::move(name_list), has_regexp_groups, component_type,
9692
33.7k
        std::move(exact_match_value));
9693
33.7k
  }
9694
9695
  // Generate regex for complex patterns
9696
4.21k
  auto [regular_expression_string, name_list] =
9697
4.21k
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
9698
4.21k
                                                                     options);
9699
4.21k
  auto pattern_string =
9700
4.21k
      url_pattern_helpers::generate_pattern_string(*part_list, options);
9701
9702
4.21k
  std::optional<typename regex_provider::regex_type> regular_expression =
9703
4.21k
      regex_provider::create_instance(regular_expression_string,
9704
4.21k
                                      options.ignore_case);
9705
4.21k
  if (!regular_expression) {
9706
0
    return tl::unexpected(errors::type_error);
9707
0
  }
9708
9709
4.21k
  return url_pattern_component<regex_provider>(
9710
4.21k
      std::move(pattern_string), std::move(*regular_expression),
9711
4.21k
      std::move(name_list), has_regexp_groups, component_type,
9712
4.21k
      std::move(exact_match_value));
9713
4.21k
}
9714
9715
template <url_pattern_regex::regex_concept regex_provider>
9716
bool url_pattern_component<regex_provider>::fast_test(
9717
    std::string_view input) const noexcept {
9718
  // Fast path for simple patterns - avoid regex evaluation
9719
  // Using if-else for better branch prediction on common cases
9720
  if (type == url_pattern_component_type::FULL_WILDCARD) {
9721
    return true;
9722
  }
9723
  if (type == url_pattern_component_type::EXACT_MATCH) {
9724
    return input == exact_match_value;
9725
  }
9726
  if (type == url_pattern_component_type::EMPTY) {
9727
    return input.empty();
9728
  }
9729
  // type == REGEXP
9730
  return regex_provider::regex_match(input, regexp);
9731
}
9732
9733
template <url_pattern_regex::regex_concept regex_provider>
9734
std::optional<std::vector<std::optional<std::string>>>
9735
url_pattern_component<regex_provider>::fast_match(
9736
    std::string_view input) const {
9737
  // Handle each type directly without redundant checks
9738
  if (type == url_pattern_component_type::FULL_WILDCARD) {
9739
    // FULL_WILDCARD always matches - capture the input (even if empty)
9740
    // If there's no group name, return empty groups
9741
    if (group_name_list.empty()) {
9742
      return std::vector<std::optional<std::string>>{};
9743
    }
9744
    // Capture the matched input (including empty strings)
9745
    return std::vector<std::optional<std::string>>{std::string(input)};
9746
  }
9747
  if (type == url_pattern_component_type::EXACT_MATCH) {
9748
    if (input == exact_match_value) {
9749
      return std::vector<std::optional<std::string>>{};
9750
    }
9751
    return std::nullopt;
9752
  }
9753
  if (type == url_pattern_component_type::EMPTY) {
9754
    if (input.empty()) {
9755
      return std::vector<std::optional<std::string>>{};
9756
    }
9757
    return std::nullopt;
9758
  }
9759
  // type == REGEXP - use regex
9760
  return regex_provider::regex_search(input, regexp);
9761
}
9762
9763
template <url_pattern_regex::regex_concept regex_provider>
9764
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::exec(
9765
    const url_pattern_input& input, const std::string_view* base_url) {
9766
  // Return the result of match given this's associated URL pattern, input, and
9767
  // baseURL if given.
9768
  return match(input, base_url);
9769
}
9770
9771
template <url_pattern_regex::regex_concept regex_provider>
9772
bool url_pattern<regex_provider>::test_components(
9773
    std::string_view protocol, std::string_view username,
9774
    std::string_view password, std::string_view hostname, std::string_view port,
9775
    std::string_view pathname, std::string_view search,
9776
    std::string_view hash) const {
9777
  return protocol_component.fast_test(protocol) &&
9778
         username_component.fast_test(username) &&
9779
         password_component.fast_test(password) &&
9780
         hostname_component.fast_test(hostname) &&
9781
         port_component.fast_test(port) &&
9782
         pathname_component.fast_test(pathname) &&
9783
         search_component.fast_test(search) && hash_component.fast_test(hash);
9784
}
9785
9786
template <url_pattern_regex::regex_concept regex_provider>
9787
result<bool> url_pattern<regex_provider>::test(
9788
    const url_pattern_input& input, const std::string_view* base_url_string) {
9789
  // If input is a URLPatternInit
9790
  if (std::holds_alternative<url_pattern_init>(input)) {
9791
    if (base_url_string) {
9792
      return tl::unexpected(errors::type_error);
9793
    }
9794
9795
    std::string protocol{}, username{}, password{}, hostname{};
9796
    std::string port{}, pathname{}, search{}, hash{};
9797
9798
    auto apply_result = url_pattern_init::process(
9799
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
9800
        protocol, username, password, hostname, port, pathname, search, hash);
9801
9802
    if (!apply_result) {
9803
      return false;
9804
    }
9805
9806
    std::string_view search_view = *apply_result->search;
9807
    if (search_view.starts_with("?")) {
9808
      search_view.remove_prefix(1);
9809
    }
9810
9811
    return test_components(*apply_result->protocol, *apply_result->username,
9812
                           *apply_result->password, *apply_result->hostname,
9813
                           *apply_result->port, *apply_result->pathname,
9814
                           search_view, *apply_result->hash);
9815
  }
9816
9817
  // URL string input path
9818
  result<url_aggregator> base_url;
9819
  if (base_url_string) {
9820
    base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
9821
    if (!base_url) {
9822
      return false;
9823
    }
9824
  }
9825
9826
  auto url =
9827
      ada::parse<url_aggregator>(std::get<std::string_view>(input),
9828
                                 base_url.has_value() ? &*base_url : nullptr);
9829
  if (!url) {
9830
    return false;
9831
  }
9832
9833
  // Extract components as string_view
9834
  auto protocol_view = url->get_protocol();
9835
  if (protocol_view.ends_with(":")) {
9836
    protocol_view.remove_suffix(1);
9837
  }
9838
9839
  auto search_view = url->get_search();
9840
  if (search_view.starts_with("?")) {
9841
    search_view.remove_prefix(1);
9842
  }
9843
9844
  auto hash_view = url->get_hash();
9845
  if (hash_view.starts_with("#")) {
9846
    hash_view.remove_prefix(1);
9847
  }
9848
9849
  return test_components(protocol_view, url->get_username(),
9850
                         url->get_password(), url->get_hostname(),
9851
                         url->get_port(), url->get_pathname(), search_view,
9852
                         hash_view);
9853
}
9854
9855
template <url_pattern_regex::regex_concept regex_provider>
9856
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::match(
9857
    const url_pattern_input& input, const std::string_view* base_url_string) {
9858
  std::string protocol{};
9859
  std::string username{};
9860
  std::string password{};
9861
  std::string hostname{};
9862
  std::string port{};
9863
  std::string pathname{};
9864
  std::string search{};
9865
  std::string hash{};
9866
9867
  // Let inputs be an empty list.
9868
  // Append input to inputs.
9869
  std::vector inputs{input};
9870
9871
  // If input is a URLPatternInit then:
9872
  if (std::holds_alternative<url_pattern_init>(input)) {
9873
    ada_log(
9874
        "url_pattern::match called with url_pattern_init and base_url_string=",
9875
        base_url_string);
9876
    // If baseURLString was given, throw a TypeError.
9877
    if (base_url_string) {
9878
      ada_log("failed to match because base_url_string was given");
9879
      return tl::unexpected(errors::type_error);
9880
    }
9881
9882
    // Let applyResult be the result of process a URLPatternInit given input,
9883
    // "url", protocol, username, password, hostname, port, pathname, search,
9884
    // and hash.
9885
    auto apply_result = url_pattern_init::process(
9886
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
9887
        protocol, username, password, hostname, port, pathname, search, hash);
9888
9889
    // If this throws an exception, catch it, and return null.
9890
    if (!apply_result.has_value()) {
9891
      ada_log("match returned std::nullopt because process threw");
9892
      return std::nullopt;
9893
    }
9894
9895
    // Set protocol to applyResult["protocol"].
9896
    ADA_ASSERT_TRUE(apply_result->protocol.has_value());
9897
    protocol = std::move(apply_result->protocol.value());
9898
9899
    // Set username to applyResult["username"].
9900
    ADA_ASSERT_TRUE(apply_result->username.has_value());
9901
    username = std::move(apply_result->username.value());
9902
9903
    // Set password to applyResult["password"].
9904
    ADA_ASSERT_TRUE(apply_result->password.has_value());
9905
    password = std::move(apply_result->password.value());
9906
9907
    // Set hostname to applyResult["hostname"].
9908
    ADA_ASSERT_TRUE(apply_result->hostname.has_value());
9909
    hostname = std::move(apply_result->hostname.value());
9910
9911
    // Set port to applyResult["port"].
9912
    ADA_ASSERT_TRUE(apply_result->port.has_value());
9913
    port = std::move(apply_result->port.value());
9914
9915
    // Set pathname to applyResult["pathname"].
9916
    ADA_ASSERT_TRUE(apply_result->pathname.has_value());
9917
    pathname = std::move(apply_result->pathname.value());
9918
9919
    // Set search to applyResult["search"].
9920
    ADA_ASSERT_TRUE(apply_result->search.has_value());
9921
    if (apply_result->search->starts_with("?")) {
9922
      search = apply_result->search->substr(1);
9923
    } else {
9924
      search = std::move(apply_result->search.value());
9925
    }
9926
9927
    // Set hash to applyResult["hash"].
9928
    ADA_ASSERT_TRUE(apply_result->hash.has_value());
9929
    ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#"));
9930
    hash = std::move(apply_result->hash.value());
9931
  } else {
9932
    ADA_ASSERT_TRUE(std::holds_alternative<std::string_view>(input));
9933
9934
    // Let baseURL be null.
9935
    result<url_aggregator> base_url;
9936
9937
    // If baseURLString was given, then:
9938
    if (base_url_string) {
9939
      // Let baseURL be the result of parsing baseURLString.
9940
      base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
9941
9942
      // If baseURL is failure, return null.
9943
      if (!base_url) {
9944
        ada_log("match returned std::nullopt because failed to parse base_url=",
9945
                *base_url_string);
9946
        return std::nullopt;
9947
      }
9948
9949
      // Append baseURLString to inputs.
9950
      inputs.emplace_back(*base_url_string);
9951
    }
9952
9953
    url_aggregator* base_url_value =
9954
        base_url.has_value() ? &*base_url : nullptr;
9955
9956
    // Set url to the result of parsing input given baseURL.
9957
    auto url = ada::parse<url_aggregator>(std::get<std::string_view>(input),
9958
                                          base_url_value);
9959
9960
    // If url is failure, return null.
9961
    if (!url) {
9962
      ada_log("match returned std::nullopt because url failed");
9963
      return std::nullopt;
9964
    }
9965
9966
    // Set protocol to url's scheme.
9967
    // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':'
9968
    // is removed. Similar work was done on workerd:
9969
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038
9970
    protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1);
9971
    // Set username to url's username.
9972
    username = url->get_username();
9973
    // Set password to url's password.
9974
    password = url->get_password();
9975
    // Set hostname to url's host, serialized, or the empty string if the value
9976
    // is null.
9977
    hostname = url->get_hostname();
9978
    // Set port to url's port, serialized, or the empty string if the value is
9979
    // null.
9980
    port = url->get_port();
9981
    // Set pathname to the result of URL path serializing url.
9982
    pathname = url->get_pathname();
9983
    // Set search to url's query or the empty string if the value is null.
9984
    // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?'
9985
    // is removed. Similar work was done on workerd:
9986
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232
9987
    if (url->has_search()) {
9988
      auto view = url->get_search();
9989
      search = view.starts_with("?") ? url->get_search().substr(1) : view;
9990
    }
9991
    // Set hash to url's fragment or the empty string if the value is null.
9992
    // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is
9993
    // removed. Similar work was done on workerd:
9994
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242
9995
    if (url->has_hash()) {
9996
      auto view = url->get_hash();
9997
      hash = view.starts_with("#") ? url->get_hash().substr(1) : view;
9998
    }
9999
  }
10000
10001
  // Use fast_match which skips regex for simple patterns (EMPTY, EXACT_MATCH,
10002
  // FULL_WILDCARD) and only falls back to regex for complex REGEXP patterns.
10003
10004
  // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol
10005
  // component's regular expression, protocol).
10006
  auto protocol_exec_result = protocol_component.fast_match(protocol);
10007
  if (!protocol_exec_result) {
10008
    return std::nullopt;
10009
  }
10010
10011
  // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username
10012
  // component's regular expression, username).
10013
  auto username_exec_result = username_component.fast_match(username);
10014
  if (!username_exec_result) {
10015
    return std::nullopt;
10016
  }
10017
10018
  // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password
10019
  // component's regular expression, password).
10020
  auto password_exec_result = password_component.fast_match(password);
10021
  if (!password_exec_result) {
10022
    return std::nullopt;
10023
  }
10024
10025
  // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname
10026
  // component's regular expression, hostname).
10027
  auto hostname_exec_result = hostname_component.fast_match(hostname);
10028
  if (!hostname_exec_result) {
10029
    return std::nullopt;
10030
  }
10031
10032
  // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's
10033
  // regular expression, port).
10034
  auto port_exec_result = port_component.fast_match(port);
10035
  if (!port_exec_result) {
10036
    return std::nullopt;
10037
  }
10038
10039
  // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname
10040
  // component's regular expression, pathname).
10041
  auto pathname_exec_result = pathname_component.fast_match(pathname);
10042
  if (!pathname_exec_result) {
10043
    return std::nullopt;
10044
  }
10045
10046
  // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's
10047
  // regular expression, search).
10048
  auto search_exec_result = search_component.fast_match(search);
10049
  if (!search_exec_result) {
10050
    return std::nullopt;
10051
  }
10052
10053
  // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's
10054
  // regular expression, hash).
10055
  auto hash_exec_result = hash_component.fast_match(hash);
10056
  if (!hash_exec_result) {
10057
    return std::nullopt;
10058
  }
10059
10060
  // Let result be a new URLPatternResult.
10061
  auto result = url_pattern_result{};
10062
  // Set result["inputs"] to inputs.
10063
  result.inputs = std::move(inputs);
10064
  // Set result["protocol"] to the result of creating a component match result
10065
  // given urlPattern's protocol component, protocol, and protocolExecResult.
10066
  result.protocol = protocol_component.create_component_match_result(
10067
      std::move(protocol), std::move(*protocol_exec_result));
10068
10069
  // Set result["username"] to the result of creating a component match result
10070
  // given urlPattern's username component, username, and usernameExecResult.
10071
  result.username = username_component.create_component_match_result(
10072
      std::move(username), std::move(*username_exec_result));
10073
10074
  // Set result["password"] to the result of creating a component match result
10075
  // given urlPattern's password component, password, and passwordExecResult.
10076
  result.password = password_component.create_component_match_result(
10077
      std::move(password), std::move(*password_exec_result));
10078
10079
  // Set result["hostname"] to the result of creating a component match result
10080
  // given urlPattern's hostname component, hostname, and hostnameExecResult.
10081
  result.hostname = hostname_component.create_component_match_result(
10082
      std::move(hostname), std::move(*hostname_exec_result));
10083
10084
  // Set result["port"] to the result of creating a component match result given
10085
  // urlPattern's port component, port, and portExecResult.
10086
  result.port = port_component.create_component_match_result(
10087
      std::move(port), std::move(*port_exec_result));
10088
10089
  // Set result["pathname"] to the result of creating a component match result
10090
  // given urlPattern's pathname component, pathname, and pathnameExecResult.
10091
  result.pathname = pathname_component.create_component_match_result(
10092
      std::move(pathname), std::move(*pathname_exec_result));
10093
10094
  // Set result["search"] to the result of creating a component match result
10095
  // given urlPattern's search component, search, and searchExecResult.
10096
  result.search = search_component.create_component_match_result(
10097
      std::move(search), std::move(*search_exec_result));
10098
10099
  // Set result["hash"] to the result of creating a component match result given
10100
  // urlPattern's hash component, hash, and hashExecResult.
10101
  result.hash = hash_component.create_component_match_result(
10102
      std::move(hash), std::move(*hash_exec_result));
10103
10104
  return result;
10105
}
10106
10107
}  // namespace ada
10108
#endif  // ADA_INCLUDE_URL_PATTERN
10109
#endif
10110
/* end file include/ada/url_pattern-inl.h */
10111
/* begin file include/ada/url_pattern_helpers-inl.h */
10112
/**
10113
 * @file url_pattern_helpers-inl.h
10114
 * @brief Declaration for the URLPattern helpers.
10115
 */
10116
#ifndef ADA_URL_PATTERN_HELPERS_INL_H
10117
#define ADA_URL_PATTERN_HELPERS_INL_H
10118
10119
#include <optional>
10120
#include <string_view>
10121
10122
10123
#if ADA_INCLUDE_URL_PATTERN
10124
namespace ada::url_pattern_helpers {
10125
#if defined(ADA_TESTING) || defined(ADA_LOGGING)
10126
0
inline std::string to_string(token_type type) {
10127
0
  switch (type) {
10128
0
    case token_type::INVALID_CHAR:
10129
0
      return "INVALID_CHAR";
10130
0
    case token_type::OPEN:
10131
0
      return "OPEN";
10132
0
    case token_type::CLOSE:
10133
0
      return "CLOSE";
10134
0
    case token_type::REGEXP:
10135
0
      return "REGEXP";
10136
0
    case token_type::NAME:
10137
0
      return "NAME";
10138
0
    case token_type::CHAR:
10139
0
      return "CHAR";
10140
0
    case token_type::ESCAPED_CHAR:
10141
0
      return "ESCAPED_CHAR";
10142
0
    case token_type::OTHER_MODIFIER:
10143
0
      return "OTHER_MODIFIER";
10144
0
    case token_type::ASTERISK:
10145
0
      return "ASTERISK";
10146
0
    case token_type::END:
10147
0
      return "END";
10148
0
    default:
10149
0
      ada::unreachable();
10150
0
  }
10151
0
}
10152
#endif  // defined(ADA_TESTING) || defined(ADA_LOGGING)
10153
10154
template <url_pattern_regex::regex_concept regex_provider>
10155
12.6k
constexpr void constructor_string_parser<regex_provider>::rewind() {
10156
  // Set parser's token index to parser's component start.
10157
12.6k
  token_index = component_start;
10158
  // Set parser's token increment to 0.
10159
12.6k
  token_increment = 0;
10160
12.6k
}
10161
10162
template <url_pattern_regex::regex_concept regex_provider>
10163
125k
constexpr bool constructor_string_parser<regex_provider>::is_hash_prefix() {
10164
  // Return the result of running is a non-special pattern char given parser,
10165
  // parser's token index and "#".
10166
125k
  return is_non_special_pattern_char(token_index, '#');
10167
125k
}
10168
10169
template <url_pattern_regex::regex_concept regex_provider>
10170
121k
constexpr bool constructor_string_parser<regex_provider>::is_search_prefix() {
10171
  // If result of running is a non-special pattern char given parser, parser's
10172
  // token index and "?" is true, then return true.
10173
121k
  if (is_non_special_pattern_char(token_index, '?')) {
10174
12
    return true;
10175
12
  }
10176
10177
  // If parser's token list[parser's token index]'s value is not "?", then
10178
  // return false.
10179
121k
  if (token_list[token_index].value != "?") {
10180
120k
    return false;
10181
120k
  }
10182
10183
  // If previous index is less than 0, then return true.
10184
1.40k
  if (token_index == 0) return true;
10185
  // Let previous index be parser's token index - 1.
10186
1.40k
  auto previous_index = token_index - 1;
10187
  // Let previous token be the result of running get a safe token given parser
10188
  // and previous index.
10189
1.40k
  auto previous_token = get_safe_token(previous_index);
10190
1.40k
  ADA_ASSERT_TRUE(previous_token);
10191
  // If any of the following are true, then return false:
10192
  // - previous token's type is "name".
10193
  // - previous token's type is "regexp".
10194
  // - previous token's type is "close".
10195
  // - previous token's type is "asterisk".
10196
1.40k
  return !(previous_token->type == token_type::NAME ||
10197
1.33k
           previous_token->type == token_type::REGEXP ||
10198
1.26k
           previous_token->type == token_type::CLOSE ||
10199
744
           previous_token->type == token_type::ASTERISK);
10200
1.40k
}
10201
10202
template <url_pattern_regex::regex_concept regex_provider>
10203
constexpr bool
10204
constructor_string_parser<regex_provider>::is_non_special_pattern_char(
10205
688k
    size_t index, uint32_t value) const {
10206
  // Let token be the result of running get a safe token given parser and index.
10207
688k
  auto token = get_safe_token(index);
10208
688k
  ADA_ASSERT_TRUE(token);
10209
10210
  // If token's value is not value, then return false.
10211
  // TODO: Remove this once we make sure get_safe_token returns a non-empty
10212
  // string.
10213
688k
  if (!token->value.empty() &&
10214
688k
      static_cast<uint32_t>(token->value[0]) != value) {
10215
657k
    return false;
10216
657k
  }
10217
10218
  // If any of the following are true:
10219
  // - token's type is "char";
10220
  // - token's type is "escaped-char"; or
10221
  // - token's type is "invalid-char",
10222
  // - then return true.
10223
31.2k
  return token->type == token_type::CHAR ||
10224
14.2k
         token->type == token_type::ESCAPED_CHAR ||
10225
14.0k
         token->type == token_type::INVALID_CHAR;
10226
688k
}
10227
10228
template <url_pattern_regex::regex_concept regex_provider>
10229
constexpr const token*
10230
711k
constructor_string_parser<regex_provider>::get_safe_token(size_t index) const {
10231
  // If index is less than parser's token list's size, then return parser's
10232
  // token list[index].
10233
711k
  if (index < token_list.size()) [[likely]] {
10234
711k
    return &token_list[index];
10235
711k
  }
10236
10237
  // Assert: parser's token list's size is greater than or equal to 1.
10238
0
  ADA_ASSERT_TRUE(!token_list.empty());
10239
10240
  // Let token be parser's token list[last index].
10241
  // Assert: token's type is "end".
10242
0
  ADA_ASSERT_TRUE(token_list.back().type == token_type::END);
10243
10244
  // Return token.
10245
0
  return &token_list.back();
10246
711k
}
10247
10248
template <url_pattern_regex::regex_concept regex_provider>
10249
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10250
281k
    const {
10251
  // If parser's token list[parser's token index]'s type is "open", then return
10252
  // true.
10253
281k
  return token_list[token_index].type == token_type::OPEN;
10254
281k
}
10255
10256
template <url_pattern_regex::regex_concept regex_provider>
10257
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10258
21.9k
    const {
10259
  // If parser's token list[parser's token index]'s type is "close", then return
10260
  // true.
10261
21.9k
  return token_list[token_index].type == token_type::CLOSE;
10262
21.9k
}
10263
10264
template <url_pattern_regex::regex_concept regex_provider>
10265
constexpr bool
10266
4.21k
constructor_string_parser<regex_provider>::next_is_authority_slashes() const {
10267
  // If the result of running is a non-special pattern char given parser,
10268
  // parser's token index + 1, and "/" is false, then return false.
10269
4.21k
  if (!is_non_special_pattern_char(token_index + 1, '/')) {
10270
0
    return false;
10271
0
  }
10272
  // If the result of running is a non-special pattern char given parser,
10273
  // parser's token index + 2, and "/" is false, then return false.
10274
4.21k
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10275
0
    return false;
10276
0
  }
10277
4.21k
  return true;
10278
4.21k
}
10279
10280
template <url_pattern_regex::regex_concept regex_provider>
10281
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10282
129k
    const {
10283
  // Return the result of running is a non-special pattern char given parser,
10284
  // parser's token index, and ":".
10285
129k
  return is_non_special_pattern_char(token_index, ':');
10286
129k
}
10287
10288
template <url_pattern_regex::regex_concept regex_provider>
10289
void constructor_string_parser<regex_provider>::change_state(State new_state,
10290
28.2k
                                                             size_t skip) {
10291
  // If parser's state is not "init", not "authority", and not "done", then set
10292
  // parser's result[parser's state] to the result of running make a component
10293
  // string given parser.
10294
28.2k
  if (state != State::INIT && state != State::AUTHORITY &&
10295
15.6k
      state != State::DONE) {
10296
15.6k
    auto value = make_component_string();
10297
    // TODO: Simplify this.
10298
15.6k
    switch (state) {
10299
4.21k
      case State::PROTOCOL: {
10300
4.21k
        result.protocol = value;
10301
4.21k
        break;
10302
0
      }
10303
0
      case State::USERNAME: {
10304
0
        result.username = value;
10305
0
        break;
10306
0
      }
10307
0
      case State::PASSWORD: {
10308
0
        result.password = value;
10309
0
        break;
10310
0
      }
10311
4.21k
      case State::HOSTNAME: {
10312
4.21k
        result.hostname = value;
10313
4.21k
        break;
10314
0
      }
10315
0
      case State::PORT: {
10316
0
        result.port = value;
10317
0
        break;
10318
0
      }
10319
6.75k
      case State::PATHNAME: {
10320
6.75k
        result.pathname = value;
10321
6.75k
        break;
10322
0
      }
10323
318
      case State::SEARCH: {
10324
318
        result.search = value;
10325
318
        break;
10326
0
      }
10327
144
      case State::HASH: {
10328
144
        result.hash = value;
10329
144
        break;
10330
0
      }
10331
0
      default:
10332
0
        ada::unreachable();
10333
15.6k
    }
10334
15.6k
  }
10335
10336
  // If parser's state is not "init" and new state is not "done", then:
10337
28.2k
  if (state != State::INIT && new_state != State::DONE) {
10338
    // If parser's state is "protocol", "authority", "username", or "password";
10339
    // new state is "port", "pathname", "search", or "hash"; and parser's
10340
    // result["hostname"] does not exist, then set parser's result["hostname"]
10341
    // to the empty string.
10342
13.1k
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10343
4.67k
         state == State::USERNAME || state == State::PASSWORD) &&
10344
8.43k
        (new_state == State::PORT || new_state == State::PATHNAME ||
10345
8.43k
         new_state == State::SEARCH || new_state == State::HASH) &&
10346
0
        !result.hostname)
10347
0
      result.hostname = "";
10348
13.1k
  }
10349
10350
  // If parser's state is "protocol", "authority", "username", "password",
10351
  // "hostname", or "port"; new state is "search" or "hash"; and parser's
10352
  // result["pathname"] does not exist, then:
10353
28.2k
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10354
19.8k
       state == State::USERNAME || state == State::PASSWORD ||
10355
19.8k
       state == State::HOSTNAME || state == State::PORT) &&
10356
12.6k
      (new_state == State::SEARCH || new_state == State::HASH) &&
10357
0
      !result.pathname) {
10358
0
    if (protocol_matches_a_special_scheme_flag) {
10359
0
      result.pathname = "/";
10360
0
    } else {
10361
      // Otherwise, set parser's result["pathname"] to the empty string.
10362
0
      result.pathname = "";
10363
0
    }
10364
0
  }
10365
10366
  // If parser's state is "protocol", "authority", "username", "password",
10367
  // "hostname", "port", or "pathname"; new state is "hash"; and parser's
10368
  // result["search"] does not exist, then set parser's result["search"] to
10369
  // the empty string.
10370
28.2k
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10371
19.8k
       state == State::USERNAME || state == State::PASSWORD ||
10372
19.8k
       state == State::HOSTNAME || state == State::PORT ||
10373
15.6k
       state == State::PATHNAME) &&
10374
19.4k
      new_state == State::HASH && !result.search) {
10375
93
    result.search = "";
10376
93
  }
10377
10378
  // Set parser's state to new state.
10379
28.2k
  state = new_state;
10380
  // Increment parser's token index by skip.
10381
28.2k
  token_index += skip;
10382
  // Set parser's component start to parser's token index.
10383
28.2k
  component_start = token_index;
10384
  // Set parser's token increment to 0.
10385
28.2k
  token_increment = 0;
10386
28.2k
}
10387
10388
template <url_pattern_regex::regex_concept regex_provider>
10389
21.5k
std::string constructor_string_parser<regex_provider>::make_component_string() {
10390
  // Assert: parser's token index is less than parser's token list's size.
10391
21.5k
  ADA_ASSERT_TRUE(token_index < token_list.size());
10392
10393
  // Let token be parser's token list[parser's token index].
10394
  // Let end index be token's index.
10395
21.5k
  const auto end_index = token_list[token_index].index;
10396
  // Let component start token be the result of running get a safe token given
10397
  // parser and parser's component start.
10398
21.5k
  const auto component_start_token = get_safe_token(component_start);
10399
21.5k
  ADA_ASSERT_TRUE(component_start_token);
10400
  // Let component start input index be component start token's index.
10401
21.5k
  const auto component_start_input_index = component_start_token->index;
10402
  // Return the code point substring from component start input index to end
10403
  // index within parser's input.
10404
21.5k
  return std::string(input.substr(component_start_input_index,
10405
21.5k
                                  end_index - component_start_input_index));
10406
21.5k
}
10407
10408
template <url_pattern_regex::regex_concept regex_provider>
10409
constexpr bool
10410
50.5k
constructor_string_parser<regex_provider>::is_an_identity_terminator() const {
10411
  // Return the result of running is a non-special pattern char given parser,
10412
  // parser's token index, and "@".
10413
50.5k
  return is_non_special_pattern_char(token_index, '@');
10414
50.5k
}
10415
10416
template <url_pattern_regex::regex_concept regex_provider>
10417
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
10418
101k
    const {
10419
  // Return the result of running is a non-special pattern char given parser,
10420
  // parser's token index, and "/".
10421
101k
  return is_non_special_pattern_char(token_index, '/');
10422
101k
}
10423
10424
template <url_pattern_regex::regex_concept regex_provider>
10425
constexpr bool constructor_string_parser<regex_provider>::is_password_prefix()
10426
0
    const {
10427
  // Return the result of running is a non-special pattern char given parser,
10428
  // parser's token index, and ":".
10429
0
  return is_non_special_pattern_char(token_index, ':');
10430
0
}
10431
10432
template <url_pattern_regex::regex_concept regex_provider>
10433
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_open()
10434
50.5k
    const {
10435
  // Return the result of running is a non-special pattern char given parser,
10436
  // parser's token index, and "[".
10437
50.5k
  return is_non_special_pattern_char(token_index, '[');
10438
50.5k
}
10439
10440
template <url_pattern_regex::regex_concept regex_provider>
10441
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
10442
50.5k
    const {
10443
  // Return the result of running is a non-special pattern char given parser,
10444
  // parser's token index, and "]".
10445
50.5k
  return is_non_special_pattern_char(token_index, ']');
10446
50.5k
}
10447
10448
template <url_pattern_regex::regex_concept regex_provider>
10449
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
10450
50.5k
    const {
10451
  // Return the result of running is a non-special pattern char given parser,
10452
  // parser's token index, and ":".
10453
50.5k
  return is_non_special_pattern_char(token_index, ':');
10454
50.5k
}
10455
10456
360k
constexpr void Tokenizer::get_next_code_point() {
10457
360k
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
10458
360k
  ADA_ASSERT_TRUE(next_index < input.size());
10459
  // this assumes that we have a valid, non-truncated UTF-8 stream.
10460
360k
  code_point = 0;
10461
360k
  size_t number_bytes = 0;
10462
360k
  unsigned char first_byte = input[next_index];
10463
10464
360k
  if ((first_byte & 0x80) == 0) {
10465
    // 1-byte character (ASCII)
10466
360k
    next_index++;
10467
360k
    code_point = first_byte;
10468
360k
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
10469
360k
            uint32_t(code_point));
10470
360k
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10471
360k
            " input.size()=", input.size());
10472
360k
    return;
10473
360k
  }
10474
0
  ada_log("Tokenizer::get_next_code_point read first byte=",
10475
0
          uint32_t(first_byte));
10476
0
  if ((first_byte & 0xE0) == 0xC0) {
10477
0
    code_point = first_byte & 0x1F;
10478
0
    number_bytes = 2;
10479
0
    ada_log("Tokenizer::get_next_code_point two bytes");
10480
0
  } else if ((first_byte & 0xF0) == 0xE0) {
10481
0
    code_point = first_byte & 0x0F;
10482
0
    number_bytes = 3;
10483
0
    ada_log("Tokenizer::get_next_code_point three bytes");
10484
0
  } else if ((first_byte & 0xF8) == 0xF0) {
10485
0
    code_point = first_byte & 0x07;
10486
0
    number_bytes = 4;
10487
0
    ada_log("Tokenizer::get_next_code_point four bytes");
10488
0
  }
10489
0
  ADA_ASSERT_TRUE(number_bytes + next_index <= input.size());
10490
10491
0
  for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) {
10492
0
    unsigned char byte = input[i];
10493
0
    ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte));
10494
0
    code_point = (code_point << 6) | (byte & 0x3F);
10495
0
  }
10496
0
  ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=",
10497
0
          uint32_t(code_point));
10498
0
  ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10499
0
          " input.size()=", input.size());
10500
0
  next_index += number_bytes;
10501
0
}
10502
10503
350k
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
10504
350k
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
10505
350k
          new_index);
10506
  // Set tokenizer's next index to index.
10507
350k
  next_index = new_index;
10508
  // Run get the next code point given tokenizer.
10509
350k
  get_next_code_point();
10510
350k
}
10511
10512
inline void Tokenizer::add_token(token_type type, size_t next_position,
10513
349k
                                 size_t value_position, size_t value_length) {
10514
349k
  ada_log("Tokenizer::add_token called with type=", to_string(type),
10515
349k
          " next_position=", next_position, " value_position=", value_position);
10516
349k
  ADA_ASSERT_TRUE(next_position >= value_position);
10517
10518
  // Let token be a new token.
10519
  // Set token's type to type.
10520
  // Set token's index to tokenizer's index.
10521
  // Set token's value to the code point substring from value position with
10522
  // length value length within tokenizer's input.
10523
  // Append token to the back of tokenizer's token list.
10524
349k
  token_list.emplace_back(type, index,
10525
349k
                          input.substr(value_position, value_length));
10526
  // Set tokenizer's index to next position.
10527
349k
  index = next_position;
10528
349k
}
10529
10530
inline void Tokenizer::add_token_with_default_length(token_type type,
10531
                                                     size_t next_position,
10532
348k
                                                     size_t value_position) {
10533
  // Let computed length be next position - value position.
10534
348k
  auto computed_length = next_position - value_position;
10535
  // Run add a token given tokenizer, type, next position, value position, and
10536
  // computed length.
10537
348k
  add_token(type, next_position, value_position, computed_length);
10538
348k
}
10539
10540
277k
inline void Tokenizer::add_token_with_defaults(token_type type) {
10541
277k
  ada_log("Tokenizer::add_token_with_defaults called with type=",
10542
277k
          to_string(type));
10543
  // Run add a token with default length given tokenizer, type, tokenizer's next
10544
  // index, and tokenizer's index.
10545
277k
  add_token_with_default_length(type, next_index, index);
10546
277k
}
10547
10548
inline ada_warn_unused std::optional<errors>
10549
Tokenizer::process_tokenizing_error(size_t next_position,
10550
15.3k
                                    size_t value_position) {
10551
  // If tokenizer's policy is "strict", then throw a TypeError.
10552
15.3k
  if (policy == token_policy::strict) {
10553
252
    ada_log("process_tokenizing_error failed with next_position=",
10554
252
            next_position, " value_position=", value_position);
10555
252
    return errors::type_error;
10556
252
  }
10557
  // Assert: tokenizer's policy is "lenient".
10558
15.0k
  ADA_ASSERT_TRUE(policy == token_policy::lenient);
10559
  // Run add a token with default length given tokenizer, "invalid-char", next
10560
  // position, and value position.
10561
15.0k
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
10562
15.0k
                                value_position);
10563
15.0k
  return std::nullopt;
10564
15.3k
}
10565
10566
template <url_pattern_encoding_callback F>
10567
22.0k
token* url_pattern_parser<F>::try_consume_modifier_token() {
10568
  // Let token be the result of running try to consume a token given parser and
10569
  // "other-modifier".
10570
22.0k
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
10571
  // If token is not null, then return token.
10572
22.0k
  if (token) return token;
10573
  // Set token to the result of running try to consume a token given parser and
10574
  // "asterisk".
10575
  // Return token.
10576
22.0k
  return try_consume_token(token_type::ASTERISK);
10577
22.0k
}
10578
10579
template <url_pattern_encoding_callback F>
10580
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
10581
161k
    const token* name_token) {
10582
  // Let token be the result of running try to consume a token given parser and
10583
  // "regexp".
10584
161k
  auto token = try_consume_token(token_type::REGEXP);
10585
  // If name token is null and token is null, then set token to the result of
10586
  // running try to consume a token given parser and "asterisk".
10587
161k
  if (!name_token && !token) {
10588
161k
    token = try_consume_token(token_type::ASTERISK);
10589
161k
  }
10590
  // Return token.
10591
161k
  return token;
10592
161k
}
10593
10594
template <url_pattern_encoding_callback F>
10595
818k
token* url_pattern_parser<F>::try_consume_token(token_type type) {
10596
818k
  ada_log("url_pattern_parser::try_consume_token called with type=",
10597
818k
          to_string(type));
10598
  // Assert: parser's index is less than parser's token list size.
10599
818k
  ADA_ASSERT_TRUE(index < tokens.size());
10600
  // Let next token be parser's token list[parser's index].
10601
818k
  auto& next_token = tokens[index];
10602
  // If next token's type is not type return null.
10603
818k
  if (next_token.type != type) return nullptr;
10604
  // Increase parser's index by 1.
10605
171k
  index++;
10606
  // Return next token.
10607
171k
  return &next_token;
10608
818k
}
10609
10610
template <url_pattern_encoding_callback F>
10611
1.99k
std::string url_pattern_parser<F>::consume_text() {
10612
  // Let result be the empty string.
10613
1.99k
  std::string result{};
10614
  // While true:
10615
8.46k
  while (true) {
10616
    // Let token be the result of running try to consume a token given parser
10617
    // and "char".
10618
8.46k
    auto token = try_consume_token(token_type::CHAR);
10619
    // If token is null, then set token to the result of running try to consume
10620
    // a token given parser and "escaped-char".
10621
8.46k
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
10622
    // If token is null, then break.
10623
8.46k
    if (!token) break;
10624
    // Append token's value to the end of result.
10625
6.47k
    result.append(token->value);
10626
6.47k
  }
10627
  // Return result.
10628
1.99k
  return result;
10629
1.99k
}
10630
10631
template <url_pattern_encoding_callback F>
10632
38.9k
bool url_pattern_parser<F>::consume_required_token(token_type type) {
10633
38.9k
  ada_log("url_pattern_parser::consume_required_token called with type=",
10634
38.9k
          to_string(type));
10635
  // Let result be the result of running try to consume a token given parser and
10636
  // type.
10637
38.9k
  return try_consume_token(type) != nullptr;
10638
38.9k
}
10639
10640
template <url_pattern_encoding_callback F>
10641
std::optional<errors>
10642
81.4k
url_pattern_parser<F>::maybe_add_part_from_the_pending_fixed_value() {
10643
  // If parser's pending fixed value is the empty string, then return.
10644
81.4k
  if (pending_fixed_value.empty()) {
10645
67.4k
    ada_log("pending_fixed_value is empty");
10646
67.4k
    return std::nullopt;
10647
67.4k
  }
10648
  // Let encoded value be the result of running parser's encoding callback given
10649
  // parser's pending fixed value.
10650
14.0k
  auto encoded_value = encoding_callback(pending_fixed_value);
10651
14.0k
  if (!encoded_value) {
10652
1.37k
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
10653
1.37k
    return encoded_value.error();
10654
1.37k
  }
10655
  // Set parser's pending fixed value to the empty string.
10656
12.6k
  pending_fixed_value.clear();
10657
  // Let part be a new part whose type is "fixed-text", value is encoded value,
10658
  // and modifier is "none".
10659
  // Append part to parser's part list.
10660
12.6k
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
10661
12.6k
                     std::move(*encoded_value),
10662
12.6k
                     url_pattern_part_modifier::none);
10663
12.6k
  return std::nullopt;
10664
14.0k
}
10665
10666
template <url_pattern_encoding_callback F>
10667
std::optional<errors> url_pattern_parser<F>::add_part(
10668
    std::string_view prefix, token* name_token, token* regexp_or_wildcard_token,
10669
22.0k
    std::string_view suffix, token* modifier_token) {
10670
  // Let modifier be "none".
10671
22.0k
  auto modifier = url_pattern_part_modifier::none;
10672
  // If modifier token is not null:
10673
22.0k
  if (modifier_token) {
10674
    // If modifier token's value is "?" then set modifier to "optional".
10675
24
    if (modifier_token->value == "?") {
10676
9
      modifier = url_pattern_part_modifier::optional;
10677
15
    } else if (modifier_token->value == "*") {
10678
      // Otherwise if modifier token's value is "*" then set modifier to
10679
      // "zero-or-more".
10680
9
      modifier = url_pattern_part_modifier::zero_or_more;
10681
9
    } else if (modifier_token->value == "+") {
10682
      // Otherwise if modifier token's value is "+" then set modifier to
10683
      // "one-or-more".
10684
6
      modifier = url_pattern_part_modifier::one_or_more;
10685
6
    }
10686
24
  }
10687
  // If name token is null and regexp or wildcard token is null and modifier
10688
  // is "none":
10689
22.0k
  if (!name_token && !regexp_or_wildcard_token &&
10690
918
      modifier == url_pattern_part_modifier::none) {
10691
    // Append prefix to the end of parser's pending fixed value.
10692
903
    pending_fixed_value.append(prefix);
10693
903
    return std::nullopt;
10694
903
  }
10695
  // Run maybe add a part from the pending fixed value given parser.
10696
21.1k
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
10697
63
    return *error;
10698
63
  }
10699
  // If name token is null and regexp or wildcard token is null:
10700
21.0k
  if (!name_token && !regexp_or_wildcard_token) {
10701
    // Assert: suffix is the empty string.
10702
0
    ADA_ASSERT_TRUE(suffix.empty());
10703
    // If prefix is the empty string, then return.
10704
0
    if (prefix.empty()) return std::nullopt;
10705
    // Let encoded value be the result of running parser's encoding callback
10706
    // given prefix.
10707
0
    auto encoded_value = encoding_callback(prefix);
10708
0
    if (!encoded_value) {
10709
0
      return encoded_value.error();
10710
0
    }
10711
    // Let part be a new part whose type is "fixed-text", value is encoded
10712
    // value, and modifier is modifier.
10713
    // Append part to parser's part list.
10714
0
    parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
10715
0
                       std::move(*encoded_value), modifier);
10716
0
    return std::nullopt;
10717
0
  }
10718
  // Let regexp value be the empty string.
10719
21.0k
  std::string regexp_value{};
10720
  // If regexp or wildcard token is null, then set regexp value to parser's
10721
  // segment wildcard regexp.
10722
21.0k
  if (!regexp_or_wildcard_token) {
10723
0
    regexp_value = segment_wildcard_regexp;
10724
21.0k
  } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) {
10725
    // Otherwise if regexp or wildcard token's type is "asterisk", then set
10726
    // regexp value to the full wildcard regexp value.
10727
21.0k
    regexp_value = ".*";
10728
21.0k
  } else {
10729
    // Otherwise set regexp value to regexp or wildcard token's value.
10730
0
    regexp_value = regexp_or_wildcard_token->value;
10731
0
  }
10732
  // Let type be "regexp".
10733
21.0k
  auto type = url_pattern_part_type::REGEXP;
10734
  // If regexp value is parser's segment wildcard regexp:
10735
21.0k
  if (regexp_value == segment_wildcard_regexp) {
10736
    // Set type to "segment-wildcard".
10737
0
    type = url_pattern_part_type::SEGMENT_WILDCARD;
10738
    // Set regexp value to the empty string.
10739
0
    regexp_value.clear();
10740
21.0k
  } else if (regexp_value == ".*") {
10741
    // Otherwise if regexp value is the full wildcard regexp value:
10742
    // Set type to "full-wildcard".
10743
21.0k
    type = url_pattern_part_type::FULL_WILDCARD;
10744
    // Set regexp value to the empty string.
10745
21.0k
    regexp_value.clear();
10746
21.0k
  }
10747
  // Let name be the empty string.
10748
21.0k
  std::string name{};
10749
  // If name token is not null, then set name to name token's value.
10750
21.0k
  if (name_token) {
10751
0
    name = name_token->value;
10752
21.0k
  } else if (regexp_or_wildcard_token != nullptr) {
10753
    // Otherwise if regexp or wildcard token is not null:
10754
    // Set name to parser's next numeric name, serialized.
10755
21.0k
    name = std::to_string(next_numeric_name);
10756
    // Increment parser's next numeric name by 1.
10757
21.0k
    next_numeric_name++;
10758
21.0k
  }
10759
  // If the result of running is a duplicate name given parser and name is
10760
  // true, then throw a TypeError.
10761
21.0k
  if (std::ranges::any_of(
10762
21.0k
          parts, [&name](const auto& part) { return part.name == name; })) {
10763
0
    return errors::type_error;
10764
0
  }
10765
  // Let encoded prefix be the result of running parser's encoding callback
10766
  // given prefix.
10767
21.0k
  auto encoded_prefix = encoding_callback(prefix);
10768
21.0k
  if (!encoded_prefix) return encoded_prefix.error();
10769
  // Let encoded suffix be the result of running parser's encoding callback
10770
  // given suffix.
10771
21.0k
  auto encoded_suffix = encoding_callback(suffix);
10772
21.0k
  if (!encoded_suffix) return encoded_suffix.error();
10773
  // Let part be a new part whose type is type, value is regexp value,
10774
  // modifier is modifier, name is name, prefix is encoded prefix, and suffix
10775
  // is encoded suffix.
10776
  // Append part to parser's part list.
10777
21.0k
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
10778
21.0k
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
10779
21.0k
  return std::nullopt;
10780
21.0k
}
10781
10782
template <url_pattern_encoding_callback F>
10783
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
10784
    std::string_view input, url_pattern_compile_component_options& options,
10785
39.5k
    F& encoding_callback) {
10786
39.5k
  ada_log("parse_pattern_string input=", input);
10787
  // Let parser be a new pattern parser whose encoding callback is encoding
10788
  // callback and segment wildcard regexp is the result of running generate a
10789
  // segment wildcard regexp given options.
10790
39.5k
  auto parser = url_pattern_parser<F>(
10791
39.5k
      encoding_callback, generate_segment_wildcard_regexp(options));
10792
  // Set parser's token list to the result of running tokenize given input and
10793
  // "strict".
10794
39.5k
  auto tokenize_result = tokenize(input, token_policy::strict);
10795
39.5k
  if (!tokenize_result) {
10796
252
    ada_log("parse_pattern_string tokenize failed");
10797
252
    return tl::unexpected(tokenize_result.error());
10798
252
  }
10799
39.3k
  parser.tokens = std::move(*tokenize_result);
10800
10801
  // While parser's index is less than parser's token list's size:
10802
198k
  while (parser.can_continue()) {
10803
    // Let char token be the result of running try to consume a token given
10804
    // parser and "char".
10805
160k
    auto char_token = parser.try_consume_token(token_type::CHAR);
10806
    // Let name token be the result of running try to consume a token given
10807
    // parser and "name".
10808
160k
    auto name_token = parser.try_consume_token(token_type::NAME);
10809
    // Let regexp or wildcard token be the result of running try to consume a
10810
    // regexp or wildcard token given parser and name token.
10811
160k
    auto regexp_or_wildcard_token =
10812
160k
        parser.try_consume_regexp_or_wildcard_token(name_token);
10813
    // If name token is not null or regexp or wildcard token is not null:
10814
160k
    if (name_token || regexp_or_wildcard_token) {
10815
      // Let prefix be the empty string.
10816
21.5k
      std::string prefix{};
10817
      // If char token is not null then set prefix to char token's value.
10818
21.5k
      if (char_token) prefix = char_token->value;
10819
      // If prefix is not the empty string and not options's prefix code point:
10820
21.5k
      if (!prefix.empty() && prefix != options.get_prefix()) {
10821
        // Append prefix to the end of parser's pending fixed value.
10822
501
        parser.pending_fixed_value.append(prefix);
10823
        // Set prefix to the empty string.
10824
501
        prefix.clear();
10825
501
      }
10826
      // Run maybe add a part from the pending fixed value given parser.
10827
21.5k
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
10828
507
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
10829
507
        return tl::unexpected(*error);
10830
507
      }
10831
      // Let modifier token be the result of running try to consume a modifier
10832
      // token given parser.
10833
21.0k
      auto modifier_token = parser.try_consume_modifier_token();
10834
      // Run add a part given parser, prefix, name token, regexp or wildcard
10835
      // token, the empty string, and modifier token.
10836
21.0k
      if (auto error =
10837
21.0k
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
10838
21.0k
                              modifier_token)) {
10839
0
        ada_log("parser.add_part failed");
10840
0
        return tl::unexpected(*error);
10841
0
      }
10842
      // Continue.
10843
21.0k
      continue;
10844
21.0k
    }
10845
10846
    // Let fixed token be char token.
10847
138k
    auto fixed_token = char_token;
10848
    // If fixed token is null, then set fixed token to the result of running try
10849
    // to consume a token given parser and "escaped-char".
10850
138k
    if (!fixed_token)
10851
40.5k
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
10852
    // If fixed token is not null:
10853
138k
    if (fixed_token) {
10854
      // Append fixed token's value to parser's pending fixed value.
10855
99.2k
      parser.pending_fixed_value.append(fixed_token->value);
10856
      // Continue.
10857
99.2k
      continue;
10858
99.2k
    }
10859
    // Let open token be the result of running try to consume a token given
10860
    // parser and "open".
10861
39.7k
    auto open_token = parser.try_consume_token(token_type::OPEN);
10862
    // If open token is not null:
10863
39.7k
    if (open_token) {
10864
      // Set prefix be the result of running consume text given parser.
10865
996
      auto prefix_ = parser.consume_text();
10866
      // Set name token to the result of running try to consume a token given
10867
      // parser and "name".
10868
996
      name_token = parser.try_consume_token(token_type::NAME);
10869
      // Set regexp or wildcard token to the result of running try to consume a
10870
      // regexp or wildcard token given parser and name token.
10871
996
      regexp_or_wildcard_token =
10872
996
          parser.try_consume_regexp_or_wildcard_token(name_token);
10873
      // Let suffix be the result of running consume text given parser.
10874
996
      auto suffix_ = parser.consume_text();
10875
      // Run consume a required token given parser and "close".
10876
996
      if (!parser.consume_required_token(token_type::CLOSE)) {
10877
30
        ada_log("parser.consume_required_token failed");
10878
30
        return tl::unexpected(errors::type_error);
10879
30
      }
10880
      // Set modifier token to the result of running try to consume a modifier
10881
      // token given parser.
10882
966
      auto modifier_token = parser.try_consume_modifier_token();
10883
      // Run add a part given parser, prefix, name token, regexp or wildcard
10884
      // token, suffix, and modifier token.
10885
966
      if (auto error =
10886
966
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
10887
966
                              suffix_, modifier_token)) {
10888
63
        return tl::unexpected(*error);
10889
63
      }
10890
      // Continue.
10891
903
      continue;
10892
966
    }
10893
    // Run maybe add a part from the pending fixed value given parser.
10894
38.7k
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
10895
804
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
10896
804
      return tl::unexpected(*error);
10897
804
    }
10898
    // Run consume a required token given parser and "end".
10899
37.9k
    if (!parser.consume_required_token(token_type::END)) {
10900
0
      return tl::unexpected(errors::type_error);
10901
0
    }
10902
37.9k
  }
10903
37.9k
  ada_log("parser.parts size is: ", parser.parts.size());
10904
  // Return parser's part list.
10905
37.9k
  return parser.parts;
10906
39.3k
}
10907
10908
template <url_pattern_regex::regex_concept regex_provider>
10909
bool protocol_component_matches_special_scheme(
10910
8.43k
    url_pattern_component<regex_provider>& component) {
10911
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
10912
8.43k
  switch (component.type) {
10913
0
    case url_pattern_component_type::EMPTY:
10914
      // Empty pattern can't match any special scheme
10915
0
      return false;
10916
8.43k
    case url_pattern_component_type::EXACT_MATCH:
10917
      // Direct string comparison for exact match patterns
10918
8.43k
      return component.exact_match_value == "http" ||
10919
8.43k
             component.exact_match_value == "https" ||
10920
0
             component.exact_match_value == "ws" ||
10921
0
             component.exact_match_value == "wss" ||
10922
0
             component.exact_match_value == "ftp";
10923
0
    case url_pattern_component_type::FULL_WILDCARD:
10924
      // Full wildcard matches everything including special schemes
10925
0
      return true;
10926
0
    case url_pattern_component_type::REGEXP:
10927
      // Fall back to regex matching for complex patterns
10928
0
      auto& regex = component.regexp;
10929
0
      return regex_provider::regex_match("http", regex) ||
10930
0
             regex_provider::regex_match("https", regex) ||
10931
0
             regex_provider::regex_match("ws", regex) ||
10932
0
             regex_provider::regex_match("wss", regex) ||
10933
0
             regex_provider::regex_match("ftp", regex);
10934
8.43k
  }
10935
0
  ada::unreachable();
10936
0
}
10937
10938
template <url_pattern_regex::regex_concept regex_provider>
10939
inline std::optional<errors> constructor_string_parser<
10940
5.87k
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
10941
5.87k
  ada_log(
10942
5.87k
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
10943
5.87k
      "flag");
10944
  // Let protocol string be the result of running make a component string given
10945
  // parser.
10946
5.87k
  auto protocol_string = make_component_string();
10947
  // Let protocol component be the result of compiling a component given
10948
  // protocol string, canonicalize a protocol, and default options.
10949
5.87k
  auto protocol_component = url_pattern_component<regex_provider>::compile(
10950
5.87k
      protocol_string, canonicalize_protocol,
10951
5.87k
      url_pattern_compile_component_options::DEFAULT);
10952
5.87k
  if (!protocol_component) {
10953
1.65k
    ada_log("url_pattern_component::compile failed for protocol_string ",
10954
1.65k
            protocol_string);
10955
1.65k
    return protocol_component.error();
10956
1.65k
  }
10957
  // If the result of running protocol component matches a special scheme given
10958
  // protocol component is true, then set parser's protocol matches a special
10959
  // scheme flag to true.
10960
4.21k
  if (protocol_component_matches_special_scheme(*protocol_component)) {
10961
4.21k
    protocol_matches_a_special_scheme_flag = true;
10962
4.21k
  }
10963
4.21k
  return std::nullopt;
10964
5.87k
}
10965
10966
template <url_pattern_regex::regex_concept regex_provider>
10967
tl::expected<url_pattern_init, errors>
10968
8.43k
constructor_string_parser<regex_provider>::parse(std::string_view input) {
10969
8.43k
  ada_log("constructor_string_parser::parse input=", input);
10970
  // Let parser be a new constructor string parser whose input is input and
10971
  // token list is the result of running tokenize given input and "lenient".
10972
8.43k
  auto token_list = tokenize(input, token_policy::lenient);
10973
8.43k
  if (!token_list) {
10974
0
    return tl::unexpected(token_list.error());
10975
0
  }
10976
8.43k
  auto parser = constructor_string_parser(input, std::move(*token_list));
10977
10978
  // While parser's token index is less than parser's token list size:
10979
290k
  while (parser.token_index < parser.token_list.size()) {
10980
    // Set parser's token increment to 1.
10981
290k
    parser.token_increment = 1;
10982
10983
    // If parser's token list[parser's token index]'s type is "end" then:
10984
290k
    if (parser.token_list[parser.token_index].type == token_type::END) {
10985
      // If parser's state is "init":
10986
9.30k
      if (parser.state == State::INIT) {
10987
        // Run rewind given parser.
10988
2.54k
        parser.rewind();
10989
        // If the result of running is a hash prefix given parser is true, then
10990
        // run change state given parser, "hash" and 1.
10991
2.54k
        if (parser.is_hash_prefix()) {
10992
0
          parser.change_state(State::HASH, 1);
10993
2.54k
        } else if (parser.is_search_prefix()) {
10994
          // Otherwise if the result of running is a search prefix given parser
10995
          // is true: Run change state given parser, "search" and 1.
10996
0
          parser.change_state(State::SEARCH, 1);
10997
2.54k
        } else {
10998
          // Run change state given parser, "pathname" and 0.
10999
2.54k
          parser.change_state(State::PATHNAME, 0);
11000
2.54k
        }
11001
        // Increment parser's token index by parser's token increment.
11002
2.54k
        parser.token_index += parser.token_increment;
11003
        // Continue.
11004
2.54k
        continue;
11005
2.54k
      }
11006
11007
6.75k
      if (parser.state == State::AUTHORITY) {
11008
        // If parser's state is "authority":
11009
        // Run rewind and set state given parser, and "hostname".
11010
0
        parser.rewind();
11011
0
        parser.change_state(State::HOSTNAME, 0);
11012
        // Increment parser's token index by parser's token increment.
11013
0
        parser.token_index += parser.token_increment;
11014
        // Continue.
11015
0
        continue;
11016
0
      }
11017
11018
      // Run change state given parser, "done" and 0.
11019
6.75k
      parser.change_state(State::DONE, 0);
11020
      // Break.
11021
6.75k
      break;
11022
6.75k
    }
11023
11024
    // If the result of running is a group open given parser is true:
11025
281k
    if (parser.is_group_open()) {
11026
      // Increment parser's group depth by 1.
11027
4.28k
      parser.group_depth += 1;
11028
      // Increment parser's token index by parser's token increment.
11029
4.28k
      parser.token_index += parser.token_increment;
11030
4.28k
    }
11031
11032
    // If parser's group depth is greater than 0:
11033
281k
    if (parser.group_depth > 0) {
11034
      // If the result of running is a group close given parser is true, then
11035
      // decrement parser's group depth by 1.
11036
21.9k
      if (parser.is_group_close()) {
11037
3.51k
        parser.group_depth -= 1;
11038
18.4k
      } else {
11039
        // Increment parser's token index by parser's token increment.
11040
18.4k
        parser.token_index += parser.token_increment;
11041
18.4k
        continue;
11042
18.4k
      }
11043
21.9k
    }
11044
11045
    // Switch on parser's state and run the associated steps:
11046
262k
    switch (parser.state) {
11047
77.0k
      case State::INIT: {
11048
        // If the result of running is a protocol suffix given parser is true:
11049
77.0k
        if (parser.is_protocol_suffix()) {
11050
          // Run rewind and set state given parser and "protocol".
11051
5.87k
          parser.rewind();
11052
5.87k
          parser.change_state(State::PROTOCOL, 0);
11053
5.87k
        }
11054
77.0k
        break;
11055
0
      }
11056
52.8k
      case State::PROTOCOL: {
11057
        // If the result of running is a protocol suffix given parser is true:
11058
52.8k
        if (parser.is_protocol_suffix()) {
11059
          // Run compute protocol matches a special scheme flag given parser.
11060
5.87k
          if (const auto error =
11061
5.87k
                  parser.compute_protocol_matches_special_scheme_flag()) {
11062
1.65k
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11063
1.65k
            return tl::unexpected(*error);
11064
1.65k
          }
11065
          // Let next state be "pathname".
11066
4.21k
          auto next_state = State::PATHNAME;
11067
          // Let skip be 1.
11068
4.21k
          auto skip = 1;
11069
          // If the result of running next is authority slashes given parser is
11070
          // true:
11071
4.21k
          if (parser.next_is_authority_slashes()) {
11072
            // Set next state to "authority".
11073
4.21k
            next_state = State::AUTHORITY;
11074
            // Set skip to 3.
11075
4.21k
            skip = 3;
11076
4.21k
          } else if (parser.protocol_matches_a_special_scheme_flag) {
11077
            // Otherwise if parser's protocol matches a special scheme flag is
11078
            // true, then set next state to "authority".
11079
0
            next_state = State::AUTHORITY;
11080
0
          }
11081
11082
          // Run change state given parser, next state, and skip.
11083
4.21k
          parser.change_state(next_state, skip);
11084
4.21k
        }
11085
51.2k
        break;
11086
52.8k
      }
11087
51.2k
      case State::AUTHORITY: {
11088
        // If the result of running is an identity terminator given parser is
11089
        // true, then run rewind and set state given parser and "username".
11090
50.5k
        if (parser.is_an_identity_terminator()) {
11091
0
          parser.rewind();
11092
0
          parser.change_state(State::USERNAME, 0);
11093
50.5k
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11094
46.3k
                   parser.is_hash_prefix()) {
11095
          // Otherwise if any of the following are true:
11096
          // - the result of running is a pathname start given parser;
11097
          // - the result of running is a search prefix given parser; or
11098
          // - the result of running is a hash prefix given parser,
11099
          // then run rewind and set state given parser and "hostname".
11100
4.21k
          parser.rewind();
11101
4.21k
          parser.change_state(State::HOSTNAME, 0);
11102
4.21k
        }
11103
50.5k
        break;
11104
52.8k
      }
11105
0
      case State::USERNAME: {
11106
        // If the result of running is a password prefix given parser is true,
11107
        // then run change state given parser, "password", and 1.
11108
0
        if (parser.is_password_prefix()) {
11109
0
          parser.change_state(State::PASSWORD, 1);
11110
0
        } else if (parser.is_an_identity_terminator()) {
11111
          // Otherwise if the result of running is an identity terminator given
11112
          // parser is true, then run change state given parser, "hostname",
11113
          // and 1.
11114
0
          parser.change_state(State::HOSTNAME, 1);
11115
0
        }
11116
0
        break;
11117
52.8k
      }
11118
0
      case State::PASSWORD: {
11119
        // If the result of running is an identity terminator given parser is
11120
        // true, then run change state given parser, "hostname", and 1.
11121
0
        if (parser.is_an_identity_terminator()) {
11122
0
          parser.change_state(State::HOSTNAME, 1);
11123
0
        }
11124
0
        break;
11125
52.8k
      }
11126
50.5k
      case State::HOSTNAME: {
11127
        // If the result of running is an IPv6 open given parser is true, then
11128
        // increment parser's hostname IPv6 bracket depth by 1.
11129
50.5k
        if (parser.is_an_ipv6_open()) {
11130
0
          parser.hostname_ipv6_bracket_depth += 1;
11131
50.5k
        } else if (parser.is_an_ipv6_close()) {
11132
          // Otherwise if the result of running is an IPv6 close given parser is
11133
          // true, then decrement parser's hostname IPv6 bracket depth by 1.
11134
0
          parser.hostname_ipv6_bracket_depth -= 1;
11135
50.5k
        } else if (parser.is_port_prefix() &&
11136
0
                   parser.hostname_ipv6_bracket_depth == 0) {
11137
          // Otherwise if the result of running is a port prefix given parser is
11138
          // true and parser's hostname IPv6 bracket depth is zero, then run
11139
          // change state given parser, "port", and 1.
11140
0
          parser.change_state(State::PORT, 1);
11141
50.5k
        } else if (parser.is_pathname_start()) {
11142
          // Otherwise if the result of running is a pathname start given parser
11143
          // is true, then run change state given parser, "pathname", and 0.
11144
4.21k
          parser.change_state(State::PATHNAME, 0);
11145
46.3k
        } else if (parser.is_search_prefix()) {
11146
          // Otherwise if the result of running is a search prefix given parser
11147
          // is true, then run change state given parser, "search", and 1.
11148
0
          parser.change_state(State::SEARCH, 1);
11149
46.3k
        } else if (parser.is_hash_prefix()) {
11150
          // Otherwise if the result of running is a hash prefix given parser is
11151
          // true, then run change state given parser, "hash", and 1.
11152
0
          parser.change_state(State::HASH, 1);
11153
0
        }
11154
11155
50.5k
        break;
11156
52.8k
      }
11157
0
      case State::PORT: {
11158
        // If the result of running is a pathname start given parser is true,
11159
        // then run change state given parser, "pathname", and 0.
11160
0
        if (parser.is_pathname_start()) {
11161
0
          parser.change_state(State::PATHNAME, 0);
11162
0
        } else if (parser.is_search_prefix()) {
11163
          // Otherwise if the result of running is a search prefix given parser
11164
          // is true, then run change state given parser, "search", and 1.
11165
0
          parser.change_state(State::SEARCH, 1);
11166
0
        } else if (parser.is_hash_prefix()) {
11167
          // Otherwise if the result of running is a hash prefix given parser is
11168
          // true, then run change state given parser, "hash", and 1.
11169
0
          parser.change_state(State::HASH, 1);
11170
0
        }
11171
0
        break;
11172
52.8k
      }
11173
26.3k
      case State::PATHNAME: {
11174
        // If the result of running is a search prefix given parser is true,
11175
        // then run change state given parser, "search", and 1.
11176
26.3k
        if (parser.is_search_prefix()) {
11177
318
          parser.change_state(State::SEARCH, 1);
11178
26.0k
        } else if (parser.is_hash_prefix()) {
11179
          // Otherwise if the result of running is a hash prefix given parser is
11180
          // true, then run change state given parser, "hash", and 1.
11181
93
          parser.change_state(State::HASH, 1);
11182
93
        }
11183
26.3k
        break;
11184
52.8k
      }
11185
4.14k
      case State::SEARCH: {
11186
        // If the result of running is a hash prefix given parser is true, then
11187
        // run change state given parser, "hash", and 1.
11188
4.14k
        if (parser.is_hash_prefix()) {
11189
51
          parser.change_state(State::HASH, 1);
11190
51
        }
11191
4.14k
        break;
11192
52.8k
      }
11193
1.31k
      case State::HASH: {
11194
        // Do nothing
11195
1.31k
        break;
11196
52.8k
      }
11197
0
      default: {
11198
        // Assert: This step is never reached.
11199
0
        unreachable();
11200
0
      }
11201
262k
    }
11202
11203
    // Increment parser's token index by parser's token increment.
11204
261k
    parser.token_index += parser.token_increment;
11205
261k
  }
11206
11207
  // If parser's result contains "hostname" and not "port", then set parser's
11208
  // result["port"] to the empty string.
11209
6.77k
  if (parser.result.hostname && !parser.result.port) {
11210
4.21k
    parser.result.port = "";
11211
4.21k
  }
11212
11213
  // Return parser's result.
11214
6.77k
  return parser.result;
11215
8.43k
}
11216
11217
}  // namespace ada::url_pattern_helpers
11218
#endif  // ADA_INCLUDE_URL_PATTERN
11219
#endif
11220
/* end file include/ada/url_pattern_helpers-inl.h */
11221
11222
// Public API
11223
/* begin file include/ada/ada_version.h */
11224
/**
11225
 * @file ada_version.h
11226
 * @brief Definitions for Ada's version number.
11227
 */
11228
#ifndef ADA_ADA_VERSION_H
11229
#define ADA_ADA_VERSION_H
11230
11231
0
#define ADA_VERSION "3.4.3"
11232
11233
namespace ada {
11234
11235
enum {
11236
  ADA_VERSION_MAJOR = 3,
11237
  ADA_VERSION_MINOR = 4,
11238
  ADA_VERSION_REVISION = 3,
11239
};
11240
11241
}  // namespace ada
11242
11243
#endif  // ADA_ADA_VERSION_H
11244
/* end file include/ada/ada_version.h */
11245
/* begin file include/ada/implementation-inl.h */
11246
/**
11247
 * @file implementation-inl.h
11248
 */
11249
#ifndef ADA_IMPLEMENTATION_INL_H
11250
#define ADA_IMPLEMENTATION_INL_H
11251
11252
11253
11254
#include <variant>
11255
#include <string_view>
11256
11257
namespace ada {
11258
11259
#if ADA_INCLUDE_URL_PATTERN
11260
template <url_pattern_regex::regex_concept regex_provider>
11261
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
11262
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
11263
                  const std::string_view* base_url,
11264
11.2k
                  const url_pattern_options* options) {
11265
11.2k
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
11266
11.2k
                                                        base_url, options);
11267
11.2k
}
11268
#endif  // ADA_INCLUDE_URL_PATTERN
11269
11270
}  // namespace ada
11271
11272
#endif  // ADA_IMPLEMENTATION_INL_H
11273
/* end file include/ada/implementation-inl.h */
11274
11275
#endif  // ADA_H
11276
/* end file include/ada.h */