Coverage Report

Created: 2026-01-10 06:29

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-01-08 10:25:21 -0500. Do not edit! */
2
/* begin file include/ada.h */
3
/**
4
 * @file ada.h
5
 * @brief Main header for the Ada URL parser library.
6
 *
7
 * This is the primary entry point for the Ada URL parser library. Including
8
 * this single header provides access to the complete Ada API, including:
9
 *
10
 * - URL parsing via `ada::parse()` function
11
 * - Two URL representations: `ada::url` and `ada::url_aggregator`
12
 * - URL search parameters via `ada::url_search_params`
13
 * - URL pattern matching via `ada::url_pattern` (URLPattern API)
14
 * - IDNA (Internationalized Domain Names) support
15
 *
16
 * @example
17
 * ```cpp
18
 *
19
 * // Parse a URL
20
 * auto url = ada::parse("https://example.com/path?query=1");
21
 * if (url) {
22
 *     std::cout << url->get_hostname(); // "example.com"
23
 * }
24
 * ```
25
 *
26
 * @see https://url.spec.whatwg.org/ - WHATWG URL Standard
27
 * @see https://github.com/ada-url/ada - Ada URL Parser GitHub Repository
28
 */
29
#ifndef ADA_H
30
#define ADA_H
31
32
/* begin file include/ada/ada_idna.h */
33
/* auto-generated on 2025-03-08 13:17:11 -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
0
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1028
0
  return !!(a[i >> 3] & (1 << (i & 7)));
1029
0
}
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
0
constexpr bool has_hex_prefix_unsafe(std::string_view input) {
1183
  // This is actually efficient code, see has_hex_prefix for the assembly.
1184
0
  constexpr bool is_little_endian = std::endian::native == std::endian::little;
1185
0
  constexpr uint16_t word0x = 0x7830;
1186
0
  uint16_t two_first_bytes =
1187
0
      static_cast<uint16_t>(input[0]) |
1188
0
      static_cast<uint16_t>((static_cast<uint16_t>(input[1]) << 8));
1189
0
  if constexpr (is_little_endian) {
1190
0
    two_first_bytes |= 0x2000;
1191
  } else {
1192
    two_first_bytes |= 0x020;
1193
  }
1194
0
  return two_first_bytes == word0x;
1195
0
}
1196
1197
0
constexpr bool has_hex_prefix(std::string_view input) {
1198
0
  return input.size() >= 2 && has_hex_prefix_unsafe(input);
1199
0
}
1200
1201
0
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1202
1203
7.17k
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1204
1205
4.42k
constexpr bool is_alpha(char x) noexcept {
1206
4.42k
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1207
4.42k
}
1208
1209
0
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1210
0
  return input.size() >= 2 &&
1211
0
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1212
0
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1213
0
                                  input[2] == '?' || input[2] == '#'));
1214
0
}
1215
1216
constexpr bool is_normalized_windows_drive_letter(
1217
0
    std::string_view input) noexcept {
1218
0
  return input.size() >= 2 && (is_alpha(input[0]) && (input[1] == ':'));
1219
0
}
1220
1221
ada_really_inline constexpr uint64_t try_parse_ipv4_fast(
1222
2.74k
    std::string_view input) noexcept {
1223
2.74k
  const char* p = input.data();
1224
2.74k
  const char* const pend = p + input.size();
1225
1226
2.74k
  uint32_t ipv4 = 0;
1227
1228
2.74k
  for (int i = 0; i < 4; ++i) {
1229
2.74k
    if (p == pend) {
1230
0
      return ipv4_fast_fail;
1231
0
    }
1232
1233
2.74k
    uint32_t val;
1234
2.74k
    char c = *p;
1235
2.74k
    if (c >= '0' && c <= '9') {
1236
0
      val = c - '0';
1237
0
      p++;
1238
2.74k
    } else {
1239
2.74k
      return ipv4_fast_fail;
1240
2.74k
    }
1241
1242
0
    if (p < pend) {
1243
0
      c = *p;
1244
0
      if (c >= '0' && c <= '9') {
1245
0
        if (val == 0) return ipv4_fast_fail;
1246
0
        val = val * 10 + (c - '0');
1247
0
        p++;
1248
0
        if (p < pend) {
1249
0
          c = *p;
1250
0
          if (c >= '0' && c <= '9') {
1251
0
            val = val * 10 + (c - '0');
1252
0
            p++;
1253
0
            if (val > 255) return ipv4_fast_fail;
1254
0
          }
1255
0
        }
1256
0
      }
1257
0
    }
1258
1259
0
    ipv4 = (ipv4 << 8) | val;
1260
1261
0
    if (i < 3) {
1262
0
      if (p == pend || *p != '.') {
1263
0
        return ipv4_fast_fail;
1264
0
      }
1265
0
      p++;
1266
0
    }
1267
0
  }
1268
1269
0
  if (p != pend) {
1270
0
    if (p == pend - 1 && *p == '.') {
1271
0
      return ipv4;
1272
0
    }
1273
0
    return ipv4_fast_fail;
1274
0
  }
1275
1276
0
  return ipv4;
1277
0
}
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
9.91k
  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 noexcept = 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) noexcept = 0;
1579
1580
  /** @private */
1581
0
  virtual ada_really_inline size_t parse_port(std::string_view view) noexcept {
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,
1657
                                    ada::scheme::type type) noexcept;
1658
1659
/**
1660
 * @private
1661
 * Defined by the URL specification, shorten a URLs paths.
1662
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1663
 * @returns Returns true if path is shortened.
1664
 */
1665
ada_really_inline bool shorten_path(std::string_view& path,
1666
                                    ada::scheme::type type) noexcept;
1667
1668
/**
1669
 * @private
1670
 *
1671
 * Parse the path from the provided input and append to the existing
1672
 * (possibly empty) path. The input cannot contain tabs and spaces: it
1673
 * is the user's responsibility to check.
1674
 *
1675
 * The input is expected to be UTF-8.
1676
 *
1677
 * @see https://url.spec.whatwg.org/
1678
 */
1679
ada_really_inline void parse_prepared_path(std::string_view input,
1680
                                           ada::scheme::type type,
1681
                                           std::string& path);
1682
1683
/**
1684
 * @private
1685
 * Remove and mutate all ASCII tab or newline characters from an input.
1686
 */
1687
ada_really_inline void remove_ascii_tab_or_newline(std::string& input) noexcept;
1688
1689
/**
1690
 * @private
1691
 * Return the substring from input going from index pos to the end.
1692
 * This function cannot throw.
1693
 */
1694
ada_really_inline constexpr std::string_view substring(std::string_view input,
1695
                                                       size_t pos) noexcept;
1696
1697
/**
1698
 * @private
1699
 * Returns true if the string_view points within the string.
1700
 */
1701
bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1702
1703
/**
1704
 * @private
1705
 * Return the substring from input going from index pos1 to the pos2 (non
1706
 * included). The length of the substring is pos2 - pos1.
1707
 */
1708
ada_really_inline constexpr std::string_view substring(std::string_view input,
1709
                                                       size_t pos1,
1710
2.74k
                                                       size_t pos2) noexcept {
1711
#if ADA_DEVELOPMENT_CHECKS
1712
  if (pos2 < pos1) {
1713
    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1714
              << std::endl;
1715
    abort();
1716
  }
1717
#endif
1718
2.74k
  return input.substr(pos1, pos2 - pos1);
1719
2.74k
}
1720
1721
/**
1722
 * @private
1723
 * Modify the string_view so that it has the new size pos, assuming that pos <=
1724
 * input.size(). This function cannot throw.
1725
 */
1726
ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1727
1728
/**
1729
 * @private
1730
 * Returns a host's delimiter location depending on the state of the instance,
1731
 * and whether a colon was found outside brackets. Used by the host parser.
1732
 */
1733
ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1734
    bool is_special, std::string_view& view) noexcept;
1735
1736
/**
1737
 * @private
1738
 * Removes leading and trailing C0 control and whitespace characters from
1739
 * string.
1740
 */
1741
void trim_c0_whitespace(std::string_view& input) noexcept;
1742
1743
/**
1744
 * @private
1745
 * @see
1746
 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1747
 */
1748
template <class url_type>
1749
ada_really_inline void strip_trailing_spaces_from_opaque_path(
1750
    url_type& url) noexcept;
1751
1752
/**
1753
 * @private
1754
 * Finds the delimiter of a view in authority state.
1755
 */
1756
ada_really_inline size_t
1757
find_authority_delimiter_special(std::string_view view) noexcept;
1758
1759
/**
1760
 * @private
1761
 * Finds the delimiter of a view in authority state.
1762
 */
1763
ada_really_inline size_t
1764
find_authority_delimiter(std::string_view view) noexcept;
1765
1766
/**
1767
 * @private
1768
 */
1769
template <typename T, typename... Args>
1770
0
inline void inner_concat(std::string& buffer, T t) {
1771
0
  buffer.append(t);
1772
0
}
Unexecuted instantiation: 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> >)
Unexecuted instantiation: void ada::helpers::inner_concat<char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
1773
1774
/**
1775
 * @private
1776
 */
1777
template <typename T, typename... Args>
1778
0
inline void inner_concat(std::string& buffer, T t, Args... args) {
1779
0
  buffer.append(t);
1780
0
  return inner_concat(buffer, args...);
1781
0
}
Unexecuted instantiation: 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> >)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Unexecuted instantiation: void ada::helpers::inner_concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
1782
1783
/**
1784
 * @private
1785
 * Concatenate the arguments and return a string.
1786
 * @returns a string
1787
 */
1788
template <typename... Args>
1789
0
std::string concat(Args... args) {
1790
0
  std::string answer;
1791
0
  inner_concat(answer, args...);
1792
0
  return answer;
1793
0
}
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<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
1794
1795
/**
1796
 * @private
1797
 * @return Number of leading zeroes.
1798
 */
1799
4.42k
inline int leading_zeroes(uint32_t input_num) noexcept {
1800
#if ADA_REGULAR_VISUAL_STUDIO
1801
  unsigned long leading_zero(0);
1802
  unsigned long in(input_num);
1803
  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
1804
#else
1805
4.42k
  return __builtin_clz(input_num);
1806
4.42k
#endif  // ADA_REGULAR_VISUAL_STUDIO
1807
4.42k
}
1808
1809
/**
1810
 * @private
1811
 * Counts the number of decimal digits necessary to represent x.
1812
 * faster than std::to_string(x).size().
1813
 * @return digit count
1814
 */
1815
0
inline int fast_digit_count(uint32_t x) noexcept {
1816
0
  auto int_log2 = [](uint32_t z) -> int {
1817
0
    return 31 - ada::helpers::leading_zeroes(z | 1);
1818
0
  };
1819
0
  // Compiles to very few instructions. Note that the
1820
0
  // table is static and thus effectively a constant.
1821
0
  // We leave it inside the function because it is meaningless
1822
0
  // outside of it (this comes at no performance cost).
1823
0
  const static uint64_t table[] = {
1824
0
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
1825
0
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
1826
0
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
1827
0
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
1828
0
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
1829
0
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
1830
0
      42949672960, 42949672960};
1831
0
  return int((x + table[int_log2(x)]) >> 32);
1832
0
}
1833
}  // namespace ada::helpers
1834
1835
#endif  // ADA_HELPERS_H
1836
/* end file include/ada/helpers.h */
1837
/* begin file include/ada/parser.h */
1838
/**
1839
 * @file parser.h
1840
 * @brief Low-level URL parsing functions.
1841
 *
1842
 * This header provides the internal URL parsing implementation. Most users
1843
 * should use `ada::parse()` from implementation.h instead of these functions
1844
 * directly.
1845
 *
1846
 * @see implementation.h for the recommended public API
1847
 */
1848
#ifndef ADA_PARSER_H
1849
#define ADA_PARSER_H
1850
1851
#include <string_view>
1852
#include <variant>
1853
1854
/* begin file include/ada/expected.h */
1855
/**
1856
 * @file expected.h
1857
 * @brief Definitions for std::expected
1858
 * @private Excluded from docs through the doxygen file.
1859
 */
1860
///
1861
// expected - An implementation of std::expected with extensions
1862
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
1863
//
1864
// Documentation available at http://tl.tartanllama.xyz/
1865
//
1866
// To the extent possible under law, the author(s) have dedicated all
1867
// copyright and related and neighboring rights to this software to the
1868
// public domain worldwide. This software is distributed without any warranty.
1869
//
1870
// You should have received a copy of the CC0 Public Domain Dedication
1871
// along with this software. If not, see
1872
// <http://creativecommons.org/publicdomain/zero/1.0/>.
1873
///
1874
1875
#ifndef TL_EXPECTED_HPP
1876
#define TL_EXPECTED_HPP
1877
1878
#define TL_EXPECTED_VERSION_MAJOR 1
1879
#define TL_EXPECTED_VERSION_MINOR 1
1880
#define TL_EXPECTED_VERSION_PATCH 0
1881
1882
#include <exception>
1883
#include <functional>
1884
#include <type_traits>
1885
#include <utility>
1886
1887
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
1888
#define TL_EXPECTED_EXCEPTIONS_ENABLED
1889
#endif
1890
1891
#if (defined(_MSC_VER) && _MSC_VER == 1900)
1892
#define TL_EXPECTED_MSVC2015
1893
#define TL_EXPECTED_MSVC2015_CONSTEXPR
1894
#else
1895
#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
1896
#endif
1897
1898
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
1899
     !defined(__clang__))
1900
#define TL_EXPECTED_GCC49
1901
#endif
1902
1903
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
1904
     !defined(__clang__))
1905
#define TL_EXPECTED_GCC54
1906
#endif
1907
1908
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
1909
     !defined(__clang__))
1910
#define TL_EXPECTED_GCC55
1911
#endif
1912
1913
#if !defined(TL_ASSERT)
1914
// can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
1915
#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
1916
#include <cassert>
1917
487k
#define TL_ASSERT(x) assert(x)
1918
#else
1919
#define TL_ASSERT(x)
1920
#endif
1921
#endif
1922
1923
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
1924
     !defined(__clang__))
1925
// GCC < 5 doesn't support overloading on const&& for member functions
1926
1927
#define TL_EXPECTED_NO_CONSTRR
1928
// GCC < 5 doesn't support some standard C++11 type traits
1929
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1930
  std::has_trivial_copy_constructor<T>
1931
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1932
  std::has_trivial_copy_assign<T>
1933
1934
// This one will be different for GCC 5.7 if it's ever supported
1935
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1936
  std::is_trivially_destructible<T>
1937
1938
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
1939
// std::vector for non-copyable types
1940
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
1941
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
1942
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
1943
namespace tl {
1944
namespace detail {
1945
template <class T>
1946
struct is_trivially_copy_constructible
1947
    : std::is_trivially_copy_constructible<T> {};
1948
#ifdef _GLIBCXX_VECTOR
1949
template <class T, class A>
1950
struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
1951
#endif
1952
}  // namespace detail
1953
}  // namespace tl
1954
#endif
1955
1956
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1957
  tl::detail::is_trivially_copy_constructible<T>
1958
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1959
  std::is_trivially_copy_assignable<T>
1960
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1961
  std::is_trivially_destructible<T>
1962
#else
1963
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1964
  std::is_trivially_copy_constructible<T>
1965
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1966
  std::is_trivially_copy_assignable<T>
1967
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1968
  std::is_trivially_destructible<T>
1969
#endif
1970
1971
#if __cplusplus > 201103L
1972
#define TL_EXPECTED_CXX14
1973
#endif
1974
1975
#ifdef TL_EXPECTED_GCC49
1976
#define TL_EXPECTED_GCC49_CONSTEXPR
1977
#else
1978
#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
1979
#endif
1980
1981
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
1982
     defined(TL_EXPECTED_GCC49))
1983
#define TL_EXPECTED_11_CONSTEXPR
1984
#else
1985
#define TL_EXPECTED_11_CONSTEXPR constexpr
1986
#endif
1987
1988
namespace tl {
1989
template <class T, class E>
1990
class expected;
1991
1992
#ifndef TL_MONOSTATE_INPLACE_MUTEX
1993
#define TL_MONOSTATE_INPLACE_MUTEX
1994
class monostate {};
1995
1996
struct in_place_t {
1997
  explicit in_place_t() = default;
1998
};
1999
static constexpr in_place_t in_place{};
2000
#endif
2001
2002
template <class E>
2003
class unexpected {
2004
 public:
2005
  static_assert(!std::is_same<E, void>::value, "E must not be void");
2006
2007
  unexpected() = delete;
2008
1.59k
  constexpr explicit unexpected(const E &e) : m_val(e) {}
2009
2010
24.8k
  constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
2011
2012
  template <class... Args, typename std::enable_if<std::is_constructible<
2013
                               E, Args &&...>::value>::type * = nullptr>
2014
  constexpr explicit unexpected(Args &&...args)
2015
6.63k
      : m_val(std::forward<Args>(args)...) {}
2016
  template <
2017
      class U, class... Args,
2018
      typename std::enable_if<std::is_constructible<
2019
          E, std::initializer_list<U> &, Args &&...>::value>::type * = nullptr>
2020
  constexpr explicit unexpected(std::initializer_list<U> l, Args &&...args)
2021
      : m_val(l, std::forward<Args>(args)...) {}
2022
2023
  constexpr const E &value() const & { return m_val; }
2024
24.5k
  TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
2025
  TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
2026
  constexpr const E &&value() const && { return std::move(m_val); }
2027
2028
 private:
2029
  E m_val;
2030
};
2031
2032
#ifdef __cpp_deduction_guides
2033
template <class E>
2034
unexpected(E) -> unexpected<E>;
2035
#endif
2036
2037
template <class E>
2038
constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2039
  return lhs.value() == rhs.value();
2040
}
2041
template <class E>
2042
constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2043
  return lhs.value() != rhs.value();
2044
}
2045
template <class E>
2046
constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2047
  return lhs.value() < rhs.value();
2048
}
2049
template <class E>
2050
constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2051
  return lhs.value() <= rhs.value();
2052
}
2053
template <class E>
2054
constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2055
  return lhs.value() > rhs.value();
2056
}
2057
template <class E>
2058
constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
2059
  return lhs.value() >= rhs.value();
2060
}
2061
2062
template <class E>
2063
unexpected<typename std::decay<E>::type> make_unexpected(E &&e) {
2064
  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
2065
}
2066
2067
struct unexpect_t {
2068
  unexpect_t() = default;
2069
};
2070
static constexpr unexpect_t unexpect{};
2071
2072
namespace detail {
2073
template <typename E>
2074
0
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
2075
0
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2076
0
  throw std::forward<E>(e);
2077
#else
2078
  (void)e;
2079
#ifdef _MSC_VER
2080
  __assume(0);
2081
#else
2082
  __builtin_unreachable();
2083
#endif
2084
#endif
2085
0
}
2086
2087
#ifndef TL_TRAITS_MUTEX
2088
#define TL_TRAITS_MUTEX
2089
// C++14-style aliases for brevity
2090
template <class T>
2091
using remove_const_t = typename std::remove_const<T>::type;
2092
template <class T>
2093
using remove_reference_t = typename std::remove_reference<T>::type;
2094
template <class T>
2095
using decay_t = typename std::decay<T>::type;
2096
template <bool E, class T = void>
2097
using enable_if_t = typename std::enable_if<E, T>::type;
2098
template <bool B, class T, class F>
2099
using conditional_t = typename std::conditional<B, T, F>::type;
2100
2101
// std::conjunction from C++17
2102
template <class...>
2103
struct conjunction : std::true_type {};
2104
template <class B>
2105
struct conjunction<B> : B {};
2106
template <class B, class... Bs>
2107
struct conjunction<B, Bs...>
2108
    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2109
2110
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2111
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2112
#endif
2113
2114
// In C++11 mode, there's an issue in libc++'s std::mem_fn
2115
// which results in a hard-error when using it in a noexcept expression
2116
// in some cases. This is a check to workaround the common failing case.
2117
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2118
template <class T>
2119
struct is_pointer_to_non_const_member_func : std::false_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...) &&>
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
template <class T, class Ret, class... Args>
2136
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &&>
2137
    : std::true_type {};
2138
2139
template <class T>
2140
struct is_const_or_const_ref : std::false_type {};
2141
template <class T>
2142
struct is_const_or_const_ref<T const &> : std::true_type {};
2143
template <class T>
2144
struct is_const_or_const_ref<T const> : std::true_type {};
2145
#endif
2146
2147
// std::invoke from C++17
2148
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2149
template <
2150
    typename Fn, typename... Args,
2151
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2152
    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2153
                             is_const_or_const_ref<Args...>::value)>,
2154
#endif
2155
    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2156
constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
2157
    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2158
    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2159
  return std::mem_fn(f)(std::forward<Args>(args)...);
2160
}
2161
2162
template <typename Fn, typename... Args,
2163
          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2164
constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
2165
    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2166
    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2167
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
2168
}
2169
2170
// std::invoke_result from C++17
2171
template <class F, class, class... Us>
2172
struct invoke_result_impl;
2173
2174
template <class F, class... Us>
2175
struct invoke_result_impl<
2176
    F,
2177
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2178
    Us...> {
2179
  using type =
2180
      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2181
};
2182
2183
template <class F, class... Us>
2184
using invoke_result = invoke_result_impl<F, void, Us...>;
2185
2186
template <class F, class... Us>
2187
using invoke_result_t = typename invoke_result<F, Us...>::type;
2188
2189
#if defined(_MSC_VER) && _MSC_VER <= 1900
2190
// TODO make a version which works with MSVC 2015
2191
template <class T, class U = T>
2192
struct is_swappable : std::true_type {};
2193
2194
template <class T, class U = T>
2195
struct is_nothrow_swappable : std::true_type {};
2196
#else
2197
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2198
namespace swap_adl_tests {
2199
// if swap ADL finds this then it would call std::swap otherwise (same
2200
// signature)
2201
struct tag {};
2202
2203
template <class T>
2204
tag swap(T &, T &);
2205
template <class T, std::size_t N>
2206
tag swap(T (&a)[N], T (&b)[N]);
2207
2208
// helper functions to test if an unqualified swap is possible, and if it
2209
// becomes std::swap
2210
template <class, class>
2211
std::false_type can_swap(...) noexcept(false);
2212
template <class T, class U,
2213
          class = decltype(swap(std::declval<T &>(), std::declval<U &>()))>
2214
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(),
2215
                                                    std::declval<U &>())));
2216
2217
template <class, class>
2218
std::false_type uses_std(...);
2219
template <class T, class U>
2220
std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag>
2221
uses_std(int);
2222
2223
template <class T>
2224
struct is_std_swap_noexcept
2225
    : std::integral_constant<bool,
2226
                             std::is_nothrow_move_constructible<T>::value &&
2227
                                 std::is_nothrow_move_assignable<T>::value> {};
2228
2229
template <class T, std::size_t N>
2230
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2231
2232
template <class T, class U>
2233
struct is_adl_swap_noexcept
2234
    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2235
}  // namespace swap_adl_tests
2236
2237
template <class T, class U = T>
2238
struct is_swappable
2239
    : std::integral_constant<
2240
          bool,
2241
          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2242
              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2243
               (std::is_move_assignable<T>::value &&
2244
                std::is_move_constructible<T>::value))> {};
2245
2246
template <class T, std::size_t N>
2247
struct is_swappable<T[N], T[N]>
2248
    : std::integral_constant<
2249
          bool,
2250
          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2251
              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2252
                   0))::value ||
2253
               is_swappable<T, T>::value)> {};
2254
2255
template <class T, class U = T>
2256
struct is_nothrow_swappable
2257
    : std::integral_constant<
2258
          bool,
2259
          is_swappable<T, U>::value &&
2260
              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2261
                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2262
               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2263
                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2264
#endif
2265
#endif
2266
2267
// Trait for checking if a type is a tl::expected
2268
template <class T>
2269
struct is_expected_impl : std::false_type {};
2270
template <class T, class E>
2271
struct is_expected_impl<expected<T, E>> : std::true_type {};
2272
template <class T>
2273
using is_expected = is_expected_impl<decay_t<T>>;
2274
2275
template <class T, class E, class U>
2276
using expected_enable_forward_value = detail::enable_if_t<
2277
    std::is_constructible<T, U &&>::value &&
2278
    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2279
    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2280
    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2281
2282
template <class T, class E, class U, class G, class UR, class GR>
2283
using expected_enable_from_other = detail::enable_if_t<
2284
    std::is_constructible<T, UR>::value &&
2285
    std::is_constructible<E, GR>::value &&
2286
    !std::is_constructible<T, expected<U, G> &>::value &&
2287
    !std::is_constructible<T, expected<U, G> &&>::value &&
2288
    !std::is_constructible<T, const expected<U, G> &>::value &&
2289
    !std::is_constructible<T, const expected<U, G> &&>::value &&
2290
    !std::is_convertible<expected<U, G> &, T>::value &&
2291
    !std::is_convertible<expected<U, G> &&, T>::value &&
2292
    !std::is_convertible<const expected<U, G> &, T>::value &&
2293
    !std::is_convertible<const expected<U, G> &&, T>::value>;
2294
2295
template <class T, class U>
2296
using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2297
2298
template <class T>
2299
using is_copy_constructible_or_void =
2300
    is_void_or<T, std::is_copy_constructible<T>>;
2301
2302
template <class T>
2303
using is_move_constructible_or_void =
2304
    is_void_or<T, std::is_move_constructible<T>>;
2305
2306
template <class T>
2307
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2308
2309
template <class T>
2310
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2311
2312
}  // namespace detail
2313
2314
namespace detail {
2315
struct no_init_t {};
2316
static constexpr no_init_t no_init{};
2317
2318
// Implements the storage of the values, and ensures that the destructor is
2319
// trivial if it can be.
2320
//
2321
// This specialization is for where neither `T` or `E` is trivially
2322
// destructible, so the destructors must be called on destruction of the
2323
// `expected`
2324
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2325
          bool = std::is_trivially_destructible<E>::value>
2326
struct expected_storage_base {
2327
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2328
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2329
2330
  template <class... Args,
2331
            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2332
                nullptr>
2333
  constexpr expected_storage_base(in_place_t, Args &&...args)
2334
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2335
2336
  template <class U, class... Args,
2337
            detail::enable_if_t<std::is_constructible<
2338
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2339
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2340
                                  Args &&...args)
2341
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2342
  template <class... Args,
2343
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2344
                nullptr>
2345
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2346
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2347
2348
  template <class U, class... Args,
2349
            detail::enable_if_t<std::is_constructible<
2350
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2351
  constexpr explicit expected_storage_base(unexpect_t,
2352
                                           std::initializer_list<U> il,
2353
                                           Args &&...args)
2354
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2355
2356
  ~expected_storage_base() {
2357
    if (m_has_val) {
2358
      m_val.~T();
2359
    } else {
2360
      m_unexpect.~unexpected<E>();
2361
    }
2362
  }
2363
  union {
2364
    T m_val;
2365
    unexpected<E> m_unexpect;
2366
    char m_no_init;
2367
  };
2368
  bool m_has_val;
2369
};
2370
2371
// This specialization is for when both `T` and `E` are trivially-destructible,
2372
// so the destructor of the `expected` can be trivial.
2373
template <class T, class E>
2374
struct expected_storage_base<T, E, true, true> {
2375
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2376
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2377
2378
  template <class... Args,
2379
            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2380
                nullptr>
2381
  constexpr expected_storage_base(in_place_t, Args &&...args)
2382
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_
2383
2384
  template <class U, class... Args,
2385
            detail::enable_if_t<std::is_constructible<
2386
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2387
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2388
                                  Args &&...args)
2389
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2390
  template <class... Args,
2391
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2392
                nullptr>
2393
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2394
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2395
2396
  template <class U, class... Args,
2397
            detail::enable_if_t<std::is_constructible<
2398
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2399
  constexpr explicit expected_storage_base(unexpect_t,
2400
                                           std::initializer_list<U> il,
2401
                                           Args &&...args)
2402
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2403
2404
  ~expected_storage_base() = default;
2405
  union {
2406
    T m_val;
2407
    unexpected<E> m_unexpect;
2408
    char m_no_init;
2409
  };
2410
  bool m_has_val;
2411
};
2412
2413
// T is trivial, E is not.
2414
template <class T, class E>
2415
struct expected_storage_base<T, E, true, false> {
2416
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2417
  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2418
      : m_no_init(), m_has_val(false) {}
2419
2420
  template <class... Args,
2421
            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2422
                nullptr>
2423
  constexpr expected_storage_base(in_place_t, Args &&...args)
2424
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2425
2426
  template <class U, class... Args,
2427
            detail::enable_if_t<std::is_constructible<
2428
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2429
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2430
                                  Args &&...args)
2431
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2432
  template <class... Args,
2433
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2434
                nullptr>
2435
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2436
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2437
2438
  template <class U, class... Args,
2439
            detail::enable_if_t<std::is_constructible<
2440
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2441
  constexpr explicit expected_storage_base(unexpect_t,
2442
                                           std::initializer_list<U> il,
2443
                                           Args &&...args)
2444
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2445
2446
  ~expected_storage_base() {
2447
    if (!m_has_val) {
2448
      m_unexpect.~unexpected<E>();
2449
    }
2450
  }
2451
2452
  union {
2453
    T m_val;
2454
    unexpected<E> m_unexpect;
2455
    char m_no_init;
2456
  };
2457
  bool m_has_val;
2458
};
2459
2460
// E is trivial, T is not.
2461
template <class T, class E>
2462
struct expected_storage_base<T, E, false, true> {
2463
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2464
0
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2465
2466
  template <class... Args,
2467
            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2468
                nullptr>
2469
  constexpr expected_storage_base(in_place_t, Args &&...args)
2470
208k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2470
2.74k
      : 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
2470
4.11k
      : 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
2470
32.9k
      : 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
2470
37.0k
      : 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
2470
46.6k
      : 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
2470
6.64k
      : 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
2470
37.0k
      : 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
2470
37.0k
      : 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
2470
4.11k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2471
2472
  template <class U, class... Args,
2473
            detail::enable_if_t<std::is_constructible<
2474
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2475
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2476
                                  Args &&...args)
2477
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2478
  template <class... Args,
2479
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2480
                nullptr>
2481
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2482
16.5k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2482
1.68k
      : 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
2482
3.27k
      : 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
2482
1.34k
      : 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
2482
216
      : 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
2482
6.86k
      : 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
2482
1.59k
      : 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
2482
1.59k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2483
2484
  template <class U, class... Args,
2485
            detail::enable_if_t<std::is_constructible<
2486
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2487
  constexpr explicit expected_storage_base(unexpect_t,
2488
                                           std::initializer_list<U> il,
2489
                                           Args &&...args)
2490
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2491
2492
224k
  ~expected_storage_base() {
2493
224k
    if (m_has_val) {
2494
208k
      m_val.~T();
2495
208k
    }
2496
224k
  }
Unexecuted instantiation: tl::detail::expected_storage_base<ada::url, ada::errors, false, true>::~expected_storage_base()
tl::detail::expected_storage_base<ada::url_aggregator, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2492
4.42k
  ~expected_storage_base() {
2493
4.42k
    if (m_has_val) {
2494
2.74k
      m_val.~T();
2495
2.74k
    }
2496
4.42k
  }
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
2492
71.3k
  ~expected_storage_base() {
2493
71.3k
    if (m_has_val) {
2494
69.9k
      m_val.~T();
2495
69.9k
    }
2496
71.3k
  }
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
2492
10.9k
  ~expected_storage_base() {
2493
10.9k
    if (m_has_val) {
2494
4.11k
      m_val.~T();
2495
4.11k
    }
2496
10.9k
  }
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
2492
46.8k
  ~expected_storage_base() {
2493
46.8k
    if (m_has_val) {
2494
46.6k
      m_val.~T();
2495
46.6k
    }
2496
46.8k
  }
tl::detail::expected_storage_base<ada::url_pattern_init, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2492
14.0k
  ~expected_storage_base() {
2493
14.0k
    if (m_has_val) {
2494
10.7k
      m_val.~T();
2495
10.7k
    }
2496
14.0k
  }
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
2492
38.6k
  ~expected_storage_base() {
2493
38.6k
    if (m_has_val) {
2494
37.0k
      m_val.~T();
2495
37.0k
    }
2496
38.6k
  }
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
2492
38.6k
  ~expected_storage_base() {
2493
38.6k
    if (m_has_val) {
2494
37.0k
      m_val.~T();
2495
37.0k
    }
2496
38.6k
  }
2497
  union {
2498
    T m_val;
2499
    unexpected<E> m_unexpect;
2500
    char m_no_init;
2501
  };
2502
  bool m_has_val;
2503
};
2504
2505
// `T` is `void`, `E` is trivially-destructible
2506
template <class E>
2507
struct expected_storage_base<void, E, false, true> {
2508
#if __GNUC__ <= 5
2509
// no constexpr for GCC 4/5 bug
2510
#else
2511
  TL_EXPECTED_MSVC2015_CONSTEXPR
2512
#endif
2513
  expected_storage_base() : m_has_val(true) {}
2514
2515
  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2516
2517
  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2518
2519
  template <class... Args,
2520
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2521
                nullptr>
2522
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2523
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2524
2525
  template <class U, class... Args,
2526
            detail::enable_if_t<std::is_constructible<
2527
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2528
  constexpr explicit expected_storage_base(unexpect_t,
2529
                                           std::initializer_list<U> il,
2530
                                           Args &&...args)
2531
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2532
2533
  ~expected_storage_base() = default;
2534
  struct dummy {};
2535
  union {
2536
    unexpected<E> m_unexpect;
2537
    dummy m_val;
2538
  };
2539
  bool m_has_val;
2540
};
2541
2542
// `T` is `void`, `E` is not trivially-destructible
2543
template <class E>
2544
struct expected_storage_base<void, E, false, false> {
2545
  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2546
  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2547
2548
  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2549
2550
  template <class... Args,
2551
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2552
                nullptr>
2553
  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2554
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2555
2556
  template <class U, class... Args,
2557
            detail::enable_if_t<std::is_constructible<
2558
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2559
  constexpr explicit expected_storage_base(unexpect_t,
2560
                                           std::initializer_list<U> il,
2561
                                           Args &&...args)
2562
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2563
2564
  ~expected_storage_base() {
2565
    if (!m_has_val) {
2566
      m_unexpect.~unexpected<E>();
2567
    }
2568
  }
2569
2570
  union {
2571
    unexpected<E> m_unexpect;
2572
    char m_dummy;
2573
  };
2574
  bool m_has_val;
2575
};
2576
2577
// This base class provides some handy member functions which can be used in
2578
// further derived classes
2579
template <class T, class E>
2580
struct expected_operations_base : expected_storage_base<T, E> {
2581
  using expected_storage_base<T, E>::expected_storage_base;
2582
2583
  template <class... Args>
2584
  void construct(Args &&...args) noexcept {
2585
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2586
    this->m_has_val = true;
2587
  }
2588
2589
  template <class Rhs>
2590
0
  void construct_with(Rhs &&rhs) noexcept {
2591
0
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2592
0
    this->m_has_val = true;
2593
0
  }
2594
2595
  template <class... Args>
2596
0
  void construct_error(Args &&...args) noexcept {
2597
0
    new (std::addressof(this->m_unexpect))
2598
0
        unexpected<E>(std::forward<Args>(args)...);
2599
0
    this->m_has_val = false;
2600
0
  }
2601
2602
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2603
2604
  // These assign overloads ensure that the most efficient assignment
2605
  // implementation is used while maintaining the strong exception guarantee.
2606
  // The problematic case is where rhs has a value, but *this does not.
2607
  //
2608
  // This overload handles the case where we can just copy-construct `T`
2609
  // directly into place without throwing.
2610
  template <class U = T,
2611
            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>
2612
                * = nullptr>
2613
  void assign(const expected_operations_base &rhs) noexcept {
2614
    if (!this->m_has_val && rhs.m_has_val) {
2615
      geterr().~unexpected<E>();
2616
      construct(rhs.get());
2617
    } else {
2618
      assign_common(rhs);
2619
    }
2620
  }
2621
2622
  // This overload handles the case where we can attempt to create a copy of
2623
  // `T`, then no-throw move it into place if the copy was successful.
2624
  template <class U = T,
2625
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2626
                                std::is_nothrow_move_constructible<U>::value>
2627
                * = nullptr>
2628
  void assign(const expected_operations_base &rhs) noexcept {
2629
    if (!this->m_has_val && rhs.m_has_val) {
2630
      T tmp = rhs.get();
2631
      geterr().~unexpected<E>();
2632
      construct(std::move(tmp));
2633
    } else {
2634
      assign_common(rhs);
2635
    }
2636
  }
2637
2638
  // This overload is the worst-case, where we have to move-construct the
2639
  // unexpected value into temporary storage, then try to copy the T into place.
2640
  // If the construction succeeds, then everything is fine, but if it throws,
2641
  // then we move the old unexpected value back into place before rethrowing the
2642
  // exception.
2643
  template <class U = T,
2644
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2645
                                !std::is_nothrow_move_constructible<U>::value>
2646
                * = nullptr>
2647
  void assign(const expected_operations_base &rhs) {
2648
    if (!this->m_has_val && rhs.m_has_val) {
2649
      auto tmp = std::move(geterr());
2650
      geterr().~unexpected<E>();
2651
2652
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2653
      try {
2654
        construct(rhs.get());
2655
      } catch (...) {
2656
        geterr() = std::move(tmp);
2657
        throw;
2658
      }
2659
#else
2660
      construct(rhs.get());
2661
#endif
2662
    } else {
2663
      assign_common(rhs);
2664
    }
2665
  }
2666
2667
  // These overloads do the same as above, but for rvalues
2668
  template <class U = T,
2669
            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>
2670
                * = nullptr>
2671
  void assign(expected_operations_base &&rhs) noexcept {
2672
    if (!this->m_has_val && rhs.m_has_val) {
2673
      geterr().~unexpected<E>();
2674
      construct(std::move(rhs).get());
2675
    } else {
2676
      assign_common(std::move(rhs));
2677
    }
2678
  }
2679
2680
  template <class U = T,
2681
            detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>
2682
                * = nullptr>
2683
  void assign(expected_operations_base &&rhs) {
2684
    if (!this->m_has_val && rhs.m_has_val) {
2685
      auto tmp = std::move(geterr());
2686
      geterr().~unexpected<E>();
2687
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2688
      try {
2689
        construct(std::move(rhs).get());
2690
      } catch (...) {
2691
        geterr() = std::move(tmp);
2692
        throw;
2693
      }
2694
#else
2695
      construct(std::move(rhs).get());
2696
#endif
2697
    } else {
2698
      assign_common(std::move(rhs));
2699
    }
2700
  }
2701
2702
#else
2703
2704
  // If exceptions are disabled then we can just copy-construct
2705
  void assign(const expected_operations_base &rhs) noexcept {
2706
    if (!this->m_has_val && rhs.m_has_val) {
2707
      geterr().~unexpected<E>();
2708
      construct(rhs.get());
2709
    } else {
2710
      assign_common(rhs);
2711
    }
2712
  }
2713
2714
  void assign(expected_operations_base &&rhs) noexcept {
2715
    if (!this->m_has_val && rhs.m_has_val) {
2716
      geterr().~unexpected<E>();
2717
      construct(std::move(rhs).get());
2718
    } else {
2719
      assign_common(std::move(rhs));
2720
    }
2721
  }
2722
2723
#endif
2724
2725
  // The common part of move/copy assigning
2726
  template <class Rhs>
2727
  void assign_common(Rhs &&rhs) {
2728
    if (this->m_has_val) {
2729
      if (rhs.m_has_val) {
2730
        get() = std::forward<Rhs>(rhs).get();
2731
      } else {
2732
        destroy_val();
2733
        construct_error(std::forward<Rhs>(rhs).geterr());
2734
      }
2735
    } else {
2736
      if (!rhs.m_has_val) {
2737
        geterr() = std::forward<Rhs>(rhs).geterr();
2738
      }
2739
    }
2740
  }
2741
2742
0
  bool has_value() const { return this->m_has_val; }
2743
2744
  TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; }
2745
0
  constexpr const T &get() const & { return this->m_val; }
2746
  TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); }
2747
#ifndef TL_EXPECTED_NO_CONSTRR
2748
  constexpr const T &&get() const && { return std::move(this->m_val); }
2749
#endif
2750
2751
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
2752
    return this->m_unexpect;
2753
  }
2754
0
  constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
2755
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
2756
    return std::move(this->m_unexpect);
2757
  }
2758
#ifndef TL_EXPECTED_NO_CONSTRR
2759
  constexpr const unexpected<E> &&geterr() const && {
2760
    return std::move(this->m_unexpect);
2761
  }
2762
#endif
2763
2764
  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2765
};
2766
2767
// This base class provides some handy member functions which can be used in
2768
// further derived classes
2769
template <class E>
2770
struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2771
  using expected_storage_base<void, E>::expected_storage_base;
2772
2773
  template <class... Args>
2774
  void construct() noexcept {
2775
    this->m_has_val = true;
2776
  }
2777
2778
  // This function doesn't use its argument, but needs it so that code in
2779
  // levels above this can work independently of whether T is void
2780
  template <class Rhs>
2781
  void construct_with(Rhs &&) noexcept {
2782
    this->m_has_val = true;
2783
  }
2784
2785
  template <class... Args>
2786
  void construct_error(Args &&...args) noexcept {
2787
    new (std::addressof(this->m_unexpect))
2788
        unexpected<E>(std::forward<Args>(args)...);
2789
    this->m_has_val = false;
2790
  }
2791
2792
  template <class Rhs>
2793
  void assign(Rhs &&rhs) noexcept {
2794
    if (!this->m_has_val) {
2795
      if (rhs.m_has_val) {
2796
        geterr().~unexpected<E>();
2797
        construct();
2798
      } else {
2799
        geterr() = std::forward<Rhs>(rhs).geterr();
2800
      }
2801
    } else {
2802
      if (!rhs.m_has_val) {
2803
        construct_error(std::forward<Rhs>(rhs).geterr());
2804
      }
2805
    }
2806
  }
2807
2808
  bool has_value() const { return this->m_has_val; }
2809
2810
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
2811
    return this->m_unexpect;
2812
  }
2813
  constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
2814
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
2815
    return std::move(this->m_unexpect);
2816
  }
2817
#ifndef TL_EXPECTED_NO_CONSTRR
2818
  constexpr const unexpected<E> &&geterr() const && {
2819
    return std::move(this->m_unexpect);
2820
  }
2821
#endif
2822
2823
  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
2824
    // no-op
2825
  }
2826
};
2827
2828
// This class manages conditionally having a trivial copy constructor
2829
// This specialization is for when T and E are trivially copy constructible
2830
template <class T, class E,
2831
          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(
2832
                                   T)>::value &&
2833
                 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
2834
struct expected_copy_base : expected_operations_base<T, E> {
2835
  using expected_operations_base<T, E>::expected_operations_base;
2836
};
2837
2838
// This specialization is for when T or E are not trivially copy constructible
2839
template <class T, class E>
2840
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
2841
  using expected_operations_base<T, E>::expected_operations_base;
2842
2843
  expected_copy_base() = default;
2844
  expected_copy_base(const expected_copy_base &rhs)
2845
0
      : expected_operations_base<T, E>(no_init) {
2846
0
    if (rhs.has_value()) {
2847
0
      this->construct_with(rhs);
2848
0
    } else {
2849
0
      this->construct_error(rhs.geterr());
2850
0
    }
2851
0
  }
2852
2853
  expected_copy_base(expected_copy_base &&rhs) = default;
2854
  expected_copy_base &operator=(const expected_copy_base &rhs) = default;
2855
  expected_copy_base &operator=(expected_copy_base &&rhs) = default;
2856
};
2857
2858
// This class manages conditionally having a trivial move constructor
2859
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
2860
// doesn't implement an analogue to std::is_trivially_move_constructible. We
2861
// have to make do with a non-trivial move constructor even if T is trivially
2862
// move constructible
2863
#ifndef TL_EXPECTED_GCC49
2864
template <class T, class E,
2865
          bool =
2866
              is_void_or<T, std::is_trivially_move_constructible<T>>::value &&
2867
              std::is_trivially_move_constructible<E>::value>
2868
struct expected_move_base : expected_copy_base<T, E> {
2869
  using expected_copy_base<T, E>::expected_copy_base;
2870
};
2871
#else
2872
template <class T, class E, bool = false>
2873
struct expected_move_base;
2874
#endif
2875
template <class T, class E>
2876
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
2877
  using expected_copy_base<T, E>::expected_copy_base;
2878
2879
  expected_move_base() = default;
2880
0
  expected_move_base(const expected_move_base &rhs) = default;
2881
2882
  expected_move_base(expected_move_base &&rhs) noexcept(
2883
      std::is_nothrow_move_constructible<T>::value)
2884
      : expected_copy_base<T, E>(no_init) {
2885
    if (rhs.has_value()) {
2886
      this->construct_with(std::move(rhs));
2887
    } else {
2888
      this->construct_error(std::move(rhs.geterr()));
2889
    }
2890
  }
2891
  expected_move_base &operator=(const expected_move_base &rhs) = default;
2892
  expected_move_base &operator=(expected_move_base &&rhs) = default;
2893
};
2894
2895
// This class manages conditionally having a trivial copy assignment operator
2896
template <
2897
    class T, class E,
2898
    bool =
2899
        is_void_or<
2900
            T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
2901
                           TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
2902
                           TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value &&
2903
        TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value &&
2904
        TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value &&
2905
        TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
2906
struct expected_copy_assign_base : expected_move_base<T, E> {
2907
  using expected_move_base<T, E>::expected_move_base;
2908
};
2909
2910
template <class T, class E>
2911
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
2912
  using expected_move_base<T, E>::expected_move_base;
2913
2914
  expected_copy_assign_base() = default;
2915
0
  expected_copy_assign_base(const expected_copy_assign_base &rhs) = default;
2916
2917
  expected_copy_assign_base(expected_copy_assign_base &&rhs) = default;
2918
  expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) {
2919
    this->assign(rhs);
2920
    return *this;
2921
  }
2922
  expected_copy_assign_base &operator=(expected_copy_assign_base &&rhs) =
2923
      default;
2924
};
2925
2926
// This class manages conditionally having a trivial move assignment operator
2927
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
2928
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
2929
// to make do with a non-trivial move assignment operator even if T is trivially
2930
// move assignable
2931
#ifndef TL_EXPECTED_GCC49
2932
template <
2933
    class T, class E,
2934
    bool = is_void_or<
2935
               T, conjunction<std::is_trivially_destructible<T>,
2936
                              std::is_trivially_move_constructible<T>,
2937
                              std::is_trivially_move_assignable<T>>>::value &&
2938
           std::is_trivially_destructible<E>::value &&
2939
           std::is_trivially_move_constructible<E>::value &&
2940
           std::is_trivially_move_assignable<E>::value>
2941
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
2942
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
2943
};
2944
#else
2945
template <class T, class E, bool = false>
2946
struct expected_move_assign_base;
2947
#endif
2948
2949
template <class T, class E>
2950
struct expected_move_assign_base<T, E, false>
2951
    : expected_copy_assign_base<T, E> {
2952
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
2953
2954
  expected_move_assign_base() = default;
2955
0
  expected_move_assign_base(const expected_move_assign_base &rhs) = default;
2956
2957
  expected_move_assign_base(expected_move_assign_base &&rhs) = default;
2958
2959
  expected_move_assign_base &operator=(const expected_move_assign_base &rhs) =
2960
      default;
2961
2962
  expected_move_assign_base &operator=(
2963
      expected_move_assign_base
2964
          &&rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
2965
                          std::is_nothrow_move_assignable<T>::value) {
2966
    this->assign(std::move(rhs));
2967
    return *this;
2968
  }
2969
};
2970
2971
// expected_delete_ctor_base will conditionally delete copy and move
2972
// constructors depending on whether T is copy/move constructible
2973
template <class T, class E,
2974
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
2975
                             std::is_copy_constructible<E>::value),
2976
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
2977
                             std::is_move_constructible<E>::value)>
2978
struct expected_delete_ctor_base {
2979
  expected_delete_ctor_base() = default;
2980
  expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
2981
  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
2982
  expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
2983
      default;
2984
  expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
2985
      default;
2986
};
2987
2988
template <class T, class E>
2989
struct expected_delete_ctor_base<T, E, true, false> {
2990
  expected_delete_ctor_base() = default;
2991
  expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
2992
  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
2993
  expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
2994
      default;
2995
  expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
2996
      default;
2997
};
2998
2999
template <class T, class E>
3000
struct expected_delete_ctor_base<T, E, false, true> {
3001
  expected_delete_ctor_base() = default;
3002
  expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
3003
  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
3004
  expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
3005
      default;
3006
  expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
3007
      default;
3008
};
3009
3010
template <class T, class E>
3011
struct expected_delete_ctor_base<T, E, false, false> {
3012
  expected_delete_ctor_base() = default;
3013
  expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
3014
  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
3015
  expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
3016
      default;
3017
  expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
3018
      default;
3019
};
3020
3021
// expected_delete_assign_base will conditionally delete copy and move
3022
// constructors depending on whether T and E are copy/move constructible +
3023
// assignable
3024
template <class T, class E,
3025
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3026
                             std::is_copy_constructible<E>::value &&
3027
                             is_copy_assignable_or_void<T>::value &&
3028
                             std::is_copy_assignable<E>::value),
3029
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3030
                             std::is_move_constructible<E>::value &&
3031
                             is_move_assignable_or_void<T>::value &&
3032
                             std::is_move_assignable<E>::value)>
3033
struct expected_delete_assign_base {
3034
  expected_delete_assign_base() = default;
3035
  expected_delete_assign_base(const expected_delete_assign_base &) = default;
3036
  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
3037
      default;
3038
  expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
3039
      default;
3040
  expected_delete_assign_base &operator=(
3041
      expected_delete_assign_base &&) noexcept = default;
3042
};
3043
3044
template <class T, class E>
3045
struct expected_delete_assign_base<T, E, true, false> {
3046
  expected_delete_assign_base() = default;
3047
  expected_delete_assign_base(const expected_delete_assign_base &) = default;
3048
  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
3049
      default;
3050
  expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
3051
      default;
3052
  expected_delete_assign_base &operator=(
3053
      expected_delete_assign_base &&) noexcept = delete;
3054
};
3055
3056
template <class T, class E>
3057
struct expected_delete_assign_base<T, E, false, true> {
3058
  expected_delete_assign_base() = default;
3059
  expected_delete_assign_base(const expected_delete_assign_base &) = default;
3060
  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
3061
      default;
3062
  expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
3063
      delete;
3064
  expected_delete_assign_base &operator=(
3065
      expected_delete_assign_base &&) noexcept = default;
3066
};
3067
3068
template <class T, class E>
3069
struct expected_delete_assign_base<T, E, false, false> {
3070
  expected_delete_assign_base() = default;
3071
  expected_delete_assign_base(const expected_delete_assign_base &) = default;
3072
  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
3073
      default;
3074
  expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
3075
      delete;
3076
  expected_delete_assign_base &operator=(
3077
      expected_delete_assign_base &&) noexcept = delete;
3078
};
3079
3080
// This is needed to be able to construct the expected_default_ctor_base which
3081
// follows, while still conditionally deleting the default constructor.
3082
struct default_constructor_tag {
3083
  explicit constexpr default_constructor_tag() = default;
3084
};
3085
3086
// expected_default_ctor_base will ensure that expected has a deleted default
3087
// consturctor if T is not default constructible.
3088
// This specialization is for when T is default constructible
3089
template <class T, class E,
3090
          bool Enable =
3091
              std::is_default_constructible<T>::value || std::is_void<T>::value>
3092
struct expected_default_ctor_base {
3093
  constexpr expected_default_ctor_base() noexcept = default;
3094
  constexpr expected_default_ctor_base(
3095
      expected_default_ctor_base const &) noexcept = default;
3096
  constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
3097
      default;
3098
  expected_default_ctor_base &operator=(
3099
      expected_default_ctor_base const &) noexcept = default;
3100
  expected_default_ctor_base &operator=(
3101
      expected_default_ctor_base &&) noexcept = default;
3102
3103
224k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
tl::detail::expected_default_ctor_base<ada::url_aggregator, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3103
4.42k
  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
3103
14.0k
  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
3103
71.3k
  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
3103
46.8k
  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
3103
10.9k
  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
3103
38.6k
  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
3103
38.6k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3104
};
3105
3106
// This specialization is for when T is not default constructible
3107
template <class T, class E>
3108
struct expected_default_ctor_base<T, E, false> {
3109
  constexpr expected_default_ctor_base() noexcept = delete;
3110
  constexpr expected_default_ctor_base(
3111
      expected_default_ctor_base const &) noexcept = default;
3112
  constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
3113
      default;
3114
  expected_default_ctor_base &operator=(
3115
      expected_default_ctor_base const &) noexcept = default;
3116
  expected_default_ctor_base &operator=(
3117
      expected_default_ctor_base &&) noexcept = default;
3118
3119
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3120
};
3121
}  // namespace detail
3122
3123
template <class E>
3124
class bad_expected_access : public std::exception {
3125
 public:
3126
0
  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3127
3128
0
  virtual const char *what() const noexcept override {
3129
0
    return "Bad expected access";
3130
0
  }
3131
3132
  const E &error() const & { return m_val; }
3133
  E &error() & { return m_val; }
3134
  const E &&error() const && { return std::move(m_val); }
3135
  E &&error() && { return std::move(m_val); }
3136
3137
 private:
3138
  E m_val;
3139
};
3140
3141
/// An `expected<T, E>` object is an object that contains the storage for
3142
/// another object and manages the lifetime of this contained object `T`.
3143
/// Alternatively it could contain the storage for another unexpected object
3144
/// `E`. The contained object may not be initialized after the expected object
3145
/// has been initialized, and may not be destroyed before the expected object
3146
/// has been destroyed. The initialization state of the contained object is
3147
/// tracked by the expected object.
3148
template <class T, class E>
3149
class expected : private detail::expected_move_assign_base<T, E>,
3150
                 private detail::expected_delete_ctor_base<T, E>,
3151
                 private detail::expected_delete_assign_base<T, E>,
3152
                 private detail::expected_default_ctor_base<T, E> {
3153
  static_assert(!std::is_reference<T>::value, "T must not be a reference");
3154
  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3155
                "T must not be in_place_t");
3156
  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3157
                "T must not be unexpect_t");
3158
  static_assert(
3159
      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3160
      "T must not be unexpected<E>");
3161
  static_assert(!std::is_reference<E>::value, "E must not be a reference");
3162
3163
185k
  T *valptr() { return std::addressof(this->m_val); }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::valptr()
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
3163
98.7k
  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
3163
86.4k
  T *valptr() { return std::addressof(this->m_val); }
3164
  const T *valptr() const { return std::addressof(this->m_val); }
3165
  unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
3166
  const unexpected<E> *errptr() const {
3167
    return std::addressof(this->m_unexpect);
3168
  }
3169
3170
  template <class U = T,
3171
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3172
294k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3173
294k
    return this->m_val;
3174
294k
  }
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3172
2.74k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3173
2.74k
    return this->m_val;
3174
2.74k
  }
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3172
69.9k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3173
69.9k
    return this->m_val;
3174
69.9k
  }
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEE3valIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3172
4.11k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3173
4.11k
    return this->m_val;
3174
4.11k
  }
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEE3valIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3172
46.6k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3173
46.6k
    return this->m_val;
3174
46.6k
  }
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3172
6.64k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3173
6.64k
    return this->m_val;
3174
6.64k
  }
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Line
Count
Source
3172
127k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3173
127k
    return this->m_val;
3174
127k
  }
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEE3valIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3172
37.0k
  TL_EXPECTED_11_CONSTEXPR U &val() {
3173
37.0k
    return this->m_val;
3174
37.0k
  }
3175
8.01k
  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
3175
1.34k
  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
3175
216
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
tl::expected<ada::url_pattern_init, ada::errors>::err()
Line
Count
Source
3175
3.27k
  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
3175
1.59k
  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
3175
1.59k
  TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
3176
3177
  template <class U = T,
3178
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3179
  constexpr const U &val() const {
3180
    return this->m_val;
3181
  }
3182
  constexpr const unexpected<E> &err() const { return this->m_unexpect; }
3183
3184
  using impl_base = detail::expected_move_assign_base<T, E>;
3185
  using ctor_base = detail::expected_default_ctor_base<T, E>;
3186
3187
 public:
3188
  typedef T value_type;
3189
  typedef E error_type;
3190
  typedef unexpected<E> unexpected_type;
3191
3192
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3193
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3194
  template <class F>
3195
  TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & {
3196
    return and_then_impl(*this, std::forward<F>(f));
3197
  }
3198
  template <class F>
3199
  TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && {
3200
    return and_then_impl(std::move(*this), std::forward<F>(f));
3201
  }
3202
  template <class F>
3203
  constexpr auto and_then(F &&f) const & {
3204
    return and_then_impl(*this, std::forward<F>(f));
3205
  }
3206
3207
#ifndef TL_EXPECTED_NO_CONSTRR
3208
  template <class F>
3209
  constexpr auto and_then(F &&f) const && {
3210
    return and_then_impl(std::move(*this), std::forward<F>(f));
3211
  }
3212
#endif
3213
3214
#else
3215
  template <class F>
3216
  TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl(
3217
      std::declval<expected &>(), std::forward<F>(f))) {
3218
    return and_then_impl(*this, std::forward<F>(f));
3219
  }
3220
  template <class F>
3221
  TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl(
3222
      std::declval<expected &&>(), std::forward<F>(f))) {
3223
    return and_then_impl(std::move(*this), std::forward<F>(f));
3224
  }
3225
  template <class F>
3226
  constexpr auto and_then(F &&f) const & -> decltype(and_then_impl(
3227
      std::declval<expected const &>(), std::forward<F>(f))) {
3228
    return and_then_impl(*this, std::forward<F>(f));
3229
  }
3230
3231
#ifndef TL_EXPECTED_NO_CONSTRR
3232
  template <class F>
3233
  constexpr auto and_then(F &&f) const && -> decltype(and_then_impl(
3234
      std::declval<expected const &&>(), std::forward<F>(f))) {
3235
    return and_then_impl(std::move(*this), std::forward<F>(f));
3236
  }
3237
#endif
3238
#endif
3239
3240
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3241
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3242
  template <class F>
3243
  TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
3244
    return expected_map_impl(*this, std::forward<F>(f));
3245
  }
3246
  template <class F>
3247
  TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && {
3248
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3249
  }
3250
  template <class F>
3251
  constexpr auto map(F &&f) const & {
3252
    return expected_map_impl(*this, std::forward<F>(f));
3253
  }
3254
  template <class F>
3255
  constexpr auto map(F &&f) const && {
3256
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3257
  }
3258
#else
3259
  template <class F>
3260
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3261
      std::declval<expected &>(), std::declval<F &&>()))
3262
  map(F &&f) & {
3263
    return expected_map_impl(*this, std::forward<F>(f));
3264
  }
3265
  template <class F>
3266
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3267
                                                      std::declval<F &&>()))
3268
  map(F &&f) && {
3269
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3270
  }
3271
  template <class F>
3272
  constexpr decltype(expected_map_impl(std::declval<const expected &>(),
3273
                                       std::declval<F &&>()))
3274
  map(F &&f) const & {
3275
    return expected_map_impl(*this, std::forward<F>(f));
3276
  }
3277
3278
#ifndef TL_EXPECTED_NO_CONSTRR
3279
  template <class F>
3280
  constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
3281
                                       std::declval<F &&>()))
3282
  map(F &&f) const && {
3283
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3284
  }
3285
#endif
3286
#endif
3287
3288
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3289
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3290
  template <class F>
3291
  TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & {
3292
    return expected_map_impl(*this, std::forward<F>(f));
3293
  }
3294
  template <class F>
3295
  TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && {
3296
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3297
  }
3298
  template <class F>
3299
  constexpr auto transform(F &&f) const & {
3300
    return expected_map_impl(*this, std::forward<F>(f));
3301
  }
3302
  template <class F>
3303
  constexpr auto transform(F &&f) const && {
3304
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3305
  }
3306
#else
3307
  template <class F>
3308
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3309
      std::declval<expected &>(), std::declval<F &&>()))
3310
  transform(F &&f) & {
3311
    return expected_map_impl(*this, std::forward<F>(f));
3312
  }
3313
  template <class F>
3314
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3315
                                                      std::declval<F &&>()))
3316
  transform(F &&f) && {
3317
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3318
  }
3319
  template <class F>
3320
  constexpr decltype(expected_map_impl(std::declval<const expected &>(),
3321
                                       std::declval<F &&>()))
3322
  transform(F &&f) const & {
3323
    return expected_map_impl(*this, std::forward<F>(f));
3324
  }
3325
3326
#ifndef TL_EXPECTED_NO_CONSTRR
3327
  template <class F>
3328
  constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
3329
                                       std::declval<F &&>()))
3330
  transform(F &&f) const && {
3331
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3332
  }
3333
#endif
3334
#endif
3335
3336
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3337
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3338
  template <class F>
3339
  TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & {
3340
    return map_error_impl(*this, std::forward<F>(f));
3341
  }
3342
  template <class F>
3343
  TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && {
3344
    return map_error_impl(std::move(*this), std::forward<F>(f));
3345
  }
3346
  template <class F>
3347
  constexpr auto map_error(F &&f) const & {
3348
    return map_error_impl(*this, std::forward<F>(f));
3349
  }
3350
  template <class F>
3351
  constexpr auto map_error(F &&f) const && {
3352
    return map_error_impl(std::move(*this), std::forward<F>(f));
3353
  }
3354
#else
3355
  template <class F>
3356
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
3357
                                                   std::declval<F &&>()))
3358
  map_error(F &&f) & {
3359
    return map_error_impl(*this, std::forward<F>(f));
3360
  }
3361
  template <class F>
3362
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
3363
                                                   std::declval<F &&>()))
3364
  map_error(F &&f) && {
3365
    return map_error_impl(std::move(*this), std::forward<F>(f));
3366
  }
3367
  template <class F>
3368
  constexpr decltype(map_error_impl(std::declval<const expected &>(),
3369
                                    std::declval<F &&>()))
3370
  map_error(F &&f) const & {
3371
    return map_error_impl(*this, std::forward<F>(f));
3372
  }
3373
3374
#ifndef TL_EXPECTED_NO_CONSTRR
3375
  template <class F>
3376
  constexpr decltype(map_error_impl(std::declval<const expected &&>(),
3377
                                    std::declval<F &&>()))
3378
  map_error(F &&f) const && {
3379
    return map_error_impl(std::move(*this), std::forward<F>(f));
3380
  }
3381
#endif
3382
#endif
3383
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3384
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3385
  template <class F>
3386
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & {
3387
    return map_error_impl(*this, std::forward<F>(f));
3388
  }
3389
  template <class F>
3390
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && {
3391
    return map_error_impl(std::move(*this), std::forward<F>(f));
3392
  }
3393
  template <class F>
3394
  constexpr auto transform_error(F &&f) const & {
3395
    return map_error_impl(*this, std::forward<F>(f));
3396
  }
3397
  template <class F>
3398
  constexpr auto transform_error(F &&f) const && {
3399
    return map_error_impl(std::move(*this), std::forward<F>(f));
3400
  }
3401
#else
3402
  template <class F>
3403
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
3404
                                                   std::declval<F &&>()))
3405
  transform_error(F &&f) & {
3406
    return map_error_impl(*this, std::forward<F>(f));
3407
  }
3408
  template <class F>
3409
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
3410
                                                   std::declval<F &&>()))
3411
  transform_error(F &&f) && {
3412
    return map_error_impl(std::move(*this), std::forward<F>(f));
3413
  }
3414
  template <class F>
3415
  constexpr decltype(map_error_impl(std::declval<const expected &>(),
3416
                                    std::declval<F &&>()))
3417
  transform_error(F &&f) const & {
3418
    return map_error_impl(*this, std::forward<F>(f));
3419
  }
3420
3421
#ifndef TL_EXPECTED_NO_CONSTRR
3422
  template <class F>
3423
  constexpr decltype(map_error_impl(std::declval<const expected &&>(),
3424
                                    std::declval<F &&>()))
3425
  transform_error(F &&f) const && {
3426
    return map_error_impl(std::move(*this), std::forward<F>(f));
3427
  }
3428
#endif
3429
#endif
3430
  template <class F>
3431
  expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
3432
    return or_else_impl(*this, std::forward<F>(f));
3433
  }
3434
3435
  template <class F>
3436
  expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && {
3437
    return or_else_impl(std::move(*this), std::forward<F>(f));
3438
  }
3439
3440
  template <class F>
3441
  expected constexpr or_else(F &&f) const & {
3442
    return or_else_impl(*this, std::forward<F>(f));
3443
  }
3444
3445
#ifndef TL_EXPECTED_NO_CONSTRR
3446
  template <class F>
3447
  expected constexpr or_else(F &&f) const && {
3448
    return or_else_impl(std::move(*this), std::forward<F>(f));
3449
  }
3450
#endif
3451
  constexpr expected() = default;
3452
0
  constexpr expected(const expected &rhs) = default;
3453
  constexpr expected(expected &&rhs) = default;
3454
  expected &operator=(const expected &rhs) = default;
3455
  expected &operator=(expected &&rhs) = default;
3456
3457
  template <class... Args,
3458
            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
3459
                nullptr>
3460
  constexpr expected(in_place_t, Args &&...args)
3461
208k
      : impl_base(in_place, std::forward<Args>(args)...),
3462
208k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3461
2.74k
      : impl_base(in_place, std::forward<Args>(args)...),
3462
2.74k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3461
4.11k
      : impl_base(in_place, std::forward<Args>(args)...),
3462
4.11k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Line
Count
Source
3461
32.9k
      : impl_base(in_place, std::forward<Args>(args)...),
3462
32.9k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA1_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Line
Count
Source
3461
37.0k
      : impl_base(in_place, std::forward<Args>(args)...),
3462
37.0k
        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
3461
46.6k
      : impl_base(in_place, std::forward<Args>(args)...),
3462
46.6k
        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
3461
6.64k
      : impl_base(in_place, std::forward<Args>(args)...),
3462
6.64k
        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
3461
37.0k
      : impl_base(in_place, std::forward<Args>(args)...),
3462
37.0k
        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
3461
37.0k
      : impl_base(in_place, std::forward<Args>(args)...),
3462
37.0k
        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
3461
4.11k
      : impl_base(in_place, std::forward<Args>(args)...),
3462
4.11k
        ctor_base(detail::default_constructor_tag{}) {}
3463
3464
  template <class U, class... Args,
3465
            detail::enable_if_t<std::is_constructible<
3466
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3467
  constexpr expected(in_place_t, std::initializer_list<U> il, Args &&...args)
3468
      : impl_base(in_place, il, std::forward<Args>(args)...),
3469
        ctor_base(detail::default_constructor_tag{}) {}
3470
3471
  template <class G = E,
3472
            detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
3473
                nullptr,
3474
            detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
3475
                nullptr>
3476
  explicit constexpr expected(const unexpected<G> &e)
3477
      : impl_base(unexpect, e.value()),
3478
        ctor_base(detail::default_constructor_tag{}) {}
3479
3480
  template <
3481
      class G = E,
3482
      detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
3483
          nullptr,
3484
      detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
3485
  constexpr expected(unexpected<G> const &e)
3486
      : impl_base(unexpect, e.value()),
3487
        ctor_base(detail::default_constructor_tag{}) {}
3488
3489
  template <
3490
      class G = E,
3491
      detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
3492
      detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
3493
  explicit constexpr expected(unexpected<G> &&e) noexcept(
3494
      std::is_nothrow_constructible<E, G &&>::value)
3495
      : impl_base(unexpect, std::move(e.value())),
3496
        ctor_base(detail::default_constructor_tag{}) {}
3497
3498
  template <
3499
      class G = E,
3500
      detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
3501
      detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
3502
  constexpr expected(unexpected<G> &&e) noexcept(
3503
      std::is_nothrow_constructible<E, G &&>::value)
3504
16.5k
      : impl_base(unexpect, std::move(e.value())),
3505
16.5k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3504
1.68k
      : impl_base(unexpect, std::move(e.value())),
3505
1.68k
        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
3504
3.27k
      : impl_base(unexpect, std::move(e.value())),
3505
3.27k
        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
3504
1.34k
      : impl_base(unexpect, std::move(e.value())),
3505
1.34k
        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
3504
216
      : impl_base(unexpect, std::move(e.value())),
3505
216
        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
3504
6.86k
      : impl_base(unexpect, std::move(e.value())),
3505
6.86k
        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
3504
1.59k
      : impl_base(unexpect, std::move(e.value())),
3505
1.59k
        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
3504
1.59k
      : impl_base(unexpect, std::move(e.value())),
3505
1.59k
        ctor_base(detail::default_constructor_tag{}) {}
3506
3507
  template <class... Args,
3508
            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
3509
                nullptr>
3510
  constexpr explicit expected(unexpect_t, Args &&...args)
3511
      : impl_base(unexpect, std::forward<Args>(args)...),
3512
        ctor_base(detail::default_constructor_tag{}) {}
3513
3514
  template <class U, class... Args,
3515
            detail::enable_if_t<std::is_constructible<
3516
                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3517
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3518
                              Args &&...args)
3519
      : impl_base(unexpect, il, std::forward<Args>(args)...),
3520
        ctor_base(detail::default_constructor_tag{}) {}
3521
3522
  template <class U, class G,
3523
            detail::enable_if_t<!(std::is_convertible<U const &, T>::value &&
3524
                                  std::is_convertible<G const &, E>::value)> * =
3525
                nullptr,
3526
            detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
3527
                * = nullptr>
3528
  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
3529
      : ctor_base(detail::default_constructor_tag{}) {
3530
    if (rhs.has_value()) {
3531
      this->construct(*rhs);
3532
    } else {
3533
      this->construct_error(rhs.error());
3534
    }
3535
  }
3536
3537
  template <class U, class G,
3538
            detail::enable_if_t<(std::is_convertible<U const &, T>::value &&
3539
                                 std::is_convertible<G const &, E>::value)> * =
3540
                nullptr,
3541
            detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
3542
                * = nullptr>
3543
  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
3544
      : ctor_base(detail::default_constructor_tag{}) {
3545
    if (rhs.has_value()) {
3546
      this->construct(*rhs);
3547
    } else {
3548
      this->construct_error(rhs.error());
3549
    }
3550
  }
3551
3552
  template <
3553
      class U, class G,
3554
      detail::enable_if_t<!(std::is_convertible<U &&, T>::value &&
3555
                            std::is_convertible<G &&, E>::value)> * = nullptr,
3556
      detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
3557
  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
3558
      : ctor_base(detail::default_constructor_tag{}) {
3559
    if (rhs.has_value()) {
3560
      this->construct(std::move(*rhs));
3561
    } else {
3562
      this->construct_error(std::move(rhs.error()));
3563
    }
3564
  }
3565
3566
  template <
3567
      class U, class G,
3568
      detail::enable_if_t<(std::is_convertible<U &&, T>::value &&
3569
                           std::is_convertible<G &&, E>::value)> * = nullptr,
3570
      detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
3571
  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
3572
      : ctor_base(detail::default_constructor_tag{}) {
3573
    if (rhs.has_value()) {
3574
      this->construct(std::move(*rhs));
3575
    } else {
3576
      this->construct_error(std::move(rhs.error()));
3577
    }
3578
  }
3579
3580
  template <
3581
      class U = T,
3582
      detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
3583
      detail::expected_enable_forward_value<T, E, U> * = nullptr>
3584
  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
3585
      : expected(in_place, std::forward<U>(v)) {}
3586
3587
  template <
3588
      class U = T,
3589
      detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
3590
      detail::expected_enable_forward_value<T, E, U> * = nullptr>
3591
  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
3592
208k
      : expected(in_place, std::forward<U>(v)) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3592
2.74k
      : 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
3592
4.11k
      : 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
3592
32.9k
      : 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
3592
37.0k
      : 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
3592
46.6k
      : 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
3592
6.64k
      : 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
3592
37.0k
      : 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
3592
37.0k
      : 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
3592
4.11k
      : expected(in_place, std::forward<U>(v)) {}
3593
3594
  template <
3595
      class U = T, class G = T,
3596
      detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * =
3597
          nullptr,
3598
      detail::enable_if_t<!std::is_void<G>::value> * = nullptr,
3599
      detail::enable_if_t<
3600
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3601
           !detail::conjunction<std::is_scalar<T>,
3602
                                std::is_same<T, detail::decay_t<U>>>::value &&
3603
           std::is_constructible<T, U>::value &&
3604
           std::is_assignable<G &, U>::value &&
3605
           std::is_nothrow_move_constructible<E>::value)> * = nullptr>
3606
  expected &operator=(U &&v) {
3607
    if (has_value()) {
3608
      val() = std::forward<U>(v);
3609
    } else {
3610
      err().~unexpected<E>();
3611
      ::new (valptr()) T(std::forward<U>(v));
3612
      this->m_has_val = true;
3613
    }
3614
3615
    return *this;
3616
  }
3617
3618
  template <
3619
      class U = T, class G = T,
3620
      detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * =
3621
          nullptr,
3622
      detail::enable_if_t<!std::is_void<U>::value> * = nullptr,
3623
      detail::enable_if_t<
3624
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3625
           !detail::conjunction<std::is_scalar<T>,
3626
                                std::is_same<T, detail::decay_t<U>>>::value &&
3627
           std::is_constructible<T, U>::value &&
3628
           std::is_assignable<G &, U>::value &&
3629
           std::is_nothrow_move_constructible<E>::value)> * = nullptr>
3630
  expected &operator=(U &&v) {
3631
    if (has_value()) {
3632
      val() = std::forward<U>(v);
3633
    } else {
3634
      auto tmp = std::move(err());
3635
      err().~unexpected<E>();
3636
3637
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3638
      try {
3639
        ::new (valptr()) T(std::forward<U>(v));
3640
        this->m_has_val = true;
3641
      } catch (...) {
3642
        err() = std::move(tmp);
3643
        throw;
3644
      }
3645
#else
3646
      ::new (valptr()) T(std::forward<U>(v));
3647
      this->m_has_val = true;
3648
#endif
3649
    }
3650
3651
    return *this;
3652
  }
3653
3654
  template <class G = E,
3655
            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3656
                                std::is_assignable<G &, G>::value> * = nullptr>
3657
  expected &operator=(const unexpected<G> &rhs) {
3658
    if (!has_value()) {
3659
      err() = rhs;
3660
    } else {
3661
      this->destroy_val();
3662
      ::new (errptr()) unexpected<E>(rhs);
3663
      this->m_has_val = false;
3664
    }
3665
3666
    return *this;
3667
  }
3668
3669
  template <class G = E,
3670
            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3671
                                std::is_move_assignable<G>::value> * = nullptr>
3672
  expected &operator=(unexpected<G> &&rhs) noexcept {
3673
    if (!has_value()) {
3674
      err() = std::move(rhs);
3675
    } else {
3676
      this->destroy_val();
3677
      ::new (errptr()) unexpected<E>(std::move(rhs));
3678
      this->m_has_val = false;
3679
    }
3680
3681
    return *this;
3682
  }
3683
3684
  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3685
                               T, Args &&...>::value> * = nullptr>
3686
  void emplace(Args &&...args) {
3687
    if (has_value()) {
3688
      val().~T();
3689
    } else {
3690
      err().~unexpected<E>();
3691
      this->m_has_val = true;
3692
    }
3693
    ::new (valptr()) T(std::forward<Args>(args)...);
3694
  }
3695
3696
  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3697
                               T, Args &&...>::value> * = nullptr>
3698
  void emplace(Args &&...args) {
3699
    if (has_value()) {
3700
      val().~T();
3701
      ::new (valptr()) T(std::forward<Args>(args)...);
3702
    } else {
3703
      auto tmp = std::move(err());
3704
      err().~unexpected<E>();
3705
3706
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3707
      try {
3708
        ::new (valptr()) T(std::forward<Args>(args)...);
3709
        this->m_has_val = true;
3710
      } catch (...) {
3711
        err() = std::move(tmp);
3712
        throw;
3713
      }
3714
#else
3715
      ::new (valptr()) T(std::forward<Args>(args)...);
3716
      this->m_has_val = true;
3717
#endif
3718
    }
3719
  }
3720
3721
  template <class U, class... Args,
3722
            detail::enable_if_t<std::is_nothrow_constructible<
3723
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3724
  void emplace(std::initializer_list<U> il, Args &&...args) {
3725
    if (has_value()) {
3726
      T t(il, std::forward<Args>(args)...);
3727
      val() = std::move(t);
3728
    } else {
3729
      err().~unexpected<E>();
3730
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3731
      this->m_has_val = true;
3732
    }
3733
  }
3734
3735
  template <class U, class... Args,
3736
            detail::enable_if_t<!std::is_nothrow_constructible<
3737
                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3738
  void emplace(std::initializer_list<U> il, Args &&...args) {
3739
    if (has_value()) {
3740
      T t(il, std::forward<Args>(args)...);
3741
      val() = std::move(t);
3742
    } else {
3743
      auto tmp = std::move(err());
3744
      err().~unexpected<E>();
3745
3746
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3747
      try {
3748
        ::new (valptr()) T(il, std::forward<Args>(args)...);
3749
        this->m_has_val = true;
3750
      } catch (...) {
3751
        err() = std::move(tmp);
3752
        throw;
3753
      }
3754
#else
3755
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3756
      this->m_has_val = true;
3757
#endif
3758
    }
3759
  }
3760
3761
 private:
3762
  using t_is_void = std::true_type;
3763
  using t_is_not_void = std::false_type;
3764
  using t_is_nothrow_move_constructible = std::true_type;
3765
  using move_constructing_t_can_throw = std::false_type;
3766
  using e_is_nothrow_move_constructible = std::true_type;
3767
  using move_constructing_e_can_throw = std::false_type;
3768
3769
  void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept {
3770
    // swapping void is a no-op
3771
  }
3772
3773
  void swap_where_both_have_value(expected &rhs, t_is_not_void) {
3774
    using std::swap;
3775
    swap(val(), rhs.val());
3776
  }
3777
3778
  void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept(
3779
      std::is_nothrow_move_constructible<E>::value) {
3780
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3781
    rhs.err().~unexpected_type();
3782
    std::swap(this->m_has_val, rhs.m_has_val);
3783
  }
3784
3785
  void swap_where_only_one_has_value(expected &rhs, t_is_not_void) {
3786
    swap_where_only_one_has_value_and_t_is_not_void(
3787
        rhs, typename std::is_nothrow_move_constructible<T>::type{},
3788
        typename std::is_nothrow_move_constructible<E>::type{});
3789
  }
3790
3791
  void swap_where_only_one_has_value_and_t_is_not_void(
3792
      expected &rhs, t_is_nothrow_move_constructible,
3793
      e_is_nothrow_move_constructible) noexcept {
3794
    auto temp = std::move(val());
3795
    val().~T();
3796
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3797
    rhs.err().~unexpected_type();
3798
    ::new (rhs.valptr()) T(std::move(temp));
3799
    std::swap(this->m_has_val, rhs.m_has_val);
3800
  }
3801
3802
  void swap_where_only_one_has_value_and_t_is_not_void(
3803
      expected &rhs, t_is_nothrow_move_constructible,
3804
      move_constructing_e_can_throw) {
3805
    auto temp = std::move(val());
3806
    val().~T();
3807
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3808
    try {
3809
      ::new (errptr()) unexpected_type(std::move(rhs.err()));
3810
      rhs.err().~unexpected_type();
3811
      ::new (rhs.valptr()) T(std::move(temp));
3812
      std::swap(this->m_has_val, rhs.m_has_val);
3813
    } catch (...) {
3814
      val() = std::move(temp);
3815
      throw;
3816
    }
3817
#else
3818
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3819
    rhs.err().~unexpected_type();
3820
    ::new (rhs.valptr()) T(std::move(temp));
3821
    std::swap(this->m_has_val, rhs.m_has_val);
3822
#endif
3823
  }
3824
3825
  void swap_where_only_one_has_value_and_t_is_not_void(
3826
      expected &rhs, move_constructing_t_can_throw,
3827
      e_is_nothrow_move_constructible) {
3828
    auto temp = std::move(rhs.err());
3829
    rhs.err().~unexpected_type();
3830
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3831
    try {
3832
      ::new (rhs.valptr()) T(std::move(val()));
3833
      val().~T();
3834
      ::new (errptr()) unexpected_type(std::move(temp));
3835
      std::swap(this->m_has_val, rhs.m_has_val);
3836
    } catch (...) {
3837
      rhs.err() = std::move(temp);
3838
      throw;
3839
    }
3840
#else
3841
    ::new (rhs.valptr()) T(std::move(val()));
3842
    val().~T();
3843
    ::new (errptr()) unexpected_type(std::move(temp));
3844
    std::swap(this->m_has_val, rhs.m_has_val);
3845
#endif
3846
  }
3847
3848
 public:
3849
  template <class OT = T, class OE = E>
3850
  detail::enable_if_t<detail::is_swappable<OT>::value &&
3851
                      detail::is_swappable<OE>::value &&
3852
                      (std::is_nothrow_move_constructible<OT>::value ||
3853
                       std::is_nothrow_move_constructible<OE>::value)>
3854
  swap(expected &rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
3855
                               detail::is_nothrow_swappable<T>::value &&
3856
                               std::is_nothrow_move_constructible<E>::value &&
3857
                               detail::is_nothrow_swappable<E>::value) {
3858
    if (has_value() && rhs.has_value()) {
3859
      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
3860
    } else if (!has_value() && rhs.has_value()) {
3861
      rhs.swap(*this);
3862
    } else if (has_value()) {
3863
      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
3864
    } else {
3865
      using std::swap;
3866
      swap(err(), rhs.err());
3867
    }
3868
  }
3869
3870
  constexpr const T *operator->() const {
3871
    TL_ASSERT(has_value());
3872
    return valptr();
3873
  }
3874
185k
  TL_EXPECTED_11_CONSTEXPR T *operator->() {
3875
185k
    TL_ASSERT(has_value());
3876
185k
    return valptr();
3877
185k
  }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::operator->()
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
3874
98.7k
  TL_EXPECTED_11_CONSTEXPR T *operator->() {
3875
98.7k
    TL_ASSERT(has_value());
3876
98.7k
    return valptr();
3877
98.7k
  }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::operator->()
Line
Count
Source
3874
86.4k
  TL_EXPECTED_11_CONSTEXPR T *operator->() {
3875
86.4k
    TL_ASSERT(has_value());
3876
86.4k
    return valptr();
3877
86.4k
  }
3878
3879
  template <class U = T,
3880
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3881
  constexpr const U &operator*() const & {
3882
    TL_ASSERT(has_value());
3883
    return val();
3884
  }
3885
  template <class U = T,
3886
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3887
294k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3888
294k
    TL_ASSERT(has_value());
3889
294k
    return val();
3890
294k
  }
Unexecuted instantiation: _ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3887
2.74k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3888
2.74k
    TL_ASSERT(has_value());
3889
2.74k
    return val();
3890
2.74k
  }
_ZNR2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3887
69.9k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3888
69.9k
    TL_ASSERT(has_value());
3889
69.9k
    return val();
3890
69.9k
  }
_ZNR2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEdeIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3887
4.11k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3888
4.11k
    TL_ASSERT(has_value());
3889
4.11k
    return val();
3890
4.11k
  }
_ZNR2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEdeIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3887
46.6k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3888
46.6k
    TL_ASSERT(has_value());
3889
46.6k
    return val();
3890
46.6k
  }
_ZNR2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3887
6.64k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3888
6.64k
    TL_ASSERT(has_value());
3889
6.64k
    return val();
3890
6.64k
  }
_ZNR2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Line
Count
Source
3887
127k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3888
127k
    TL_ASSERT(has_value());
3889
127k
    return val();
3890
127k
  }
_ZNR2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEdeIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3887
37.0k
  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3888
37.0k
    TL_ASSERT(has_value());
3889
37.0k
    return val();
3890
37.0k
  }
3891
  template <class U = T,
3892
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3893
  constexpr const U &&operator*() const && {
3894
    TL_ASSERT(has_value());
3895
    return std::move(val());
3896
  }
3897
  template <class U = T,
3898
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3899
  TL_EXPECTED_11_CONSTEXPR U &&operator*() && {
3900
    TL_ASSERT(has_value());
3901
    return std::move(val());
3902
  }
3903
3904
487k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::has_value() const
tl::expected<ada::url_aggregator, ada::errors>::has_value() const
Line
Count
Source
3904
2.74k
  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
3904
71.3k
  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
3904
4.11k
  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
3904
46.8k
  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
3904
108k
  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
3904
215k
  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
3904
38.6k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
3905
224k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator bool() const
tl::expected<ada::url_aggregator, ada::errors>::operator bool() const
Line
Count
Source
3905
4.42k
  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
3905
71.3k
  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
3905
10.9k
  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
3905
46.8k
  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
3905
14.0k
  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
3905
38.6k
  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
3905
38.6k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
3906
3907
  template <class U = T,
3908
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3909
  TL_EXPECTED_11_CONSTEXPR const U &value() const & {
3910
    if (!has_value())
3911
      detail::throw_exception(bad_expected_access<E>(err().value()));
3912
    return val();
3913
  }
3914
  template <class U = T,
3915
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3916
0
  TL_EXPECTED_11_CONSTEXPR U &value() & {
3917
0
    if (!has_value())
3918
0
      detail::throw_exception(bad_expected_access<E>(err().value()));
3919
0
    return val();
3920
0
  }
3921
  template <class U = T,
3922
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3923
  TL_EXPECTED_11_CONSTEXPR const U &&value() const && {
3924
    if (!has_value())
3925
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
3926
    return std::move(val());
3927
  }
3928
  template <class U = T,
3929
            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3930
  TL_EXPECTED_11_CONSTEXPR U &&value() && {
3931
    if (!has_value())
3932
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
3933
    return std::move(val());
3934
  }
3935
3936
  constexpr const E &error() const & {
3937
    TL_ASSERT(!has_value());
3938
    return err().value();
3939
  }
3940
8.01k
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3941
8.01k
    TL_ASSERT(!has_value());
3942
8.01k
    return err().value();
3943
8.01k
  }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::error() &
Line
Count
Source
3940
1.34k
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3941
1.34k
    TL_ASSERT(!has_value());
3942
1.34k
    return err().value();
3943
1.34k
  }
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::error() &
Line
Count
Source
3940
216
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3941
216
    TL_ASSERT(!has_value());
3942
216
    return err().value();
3943
216
  }
tl::expected<ada::url_pattern_init, ada::errors>::error() &
Line
Count
Source
3940
3.27k
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3941
3.27k
    TL_ASSERT(!has_value());
3942
3.27k
    return err().value();
3943
3.27k
  }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::error() &
Line
Count
Source
3940
1.59k
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3941
1.59k
    TL_ASSERT(!has_value());
3942
1.59k
    return err().value();
3943
1.59k
  }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::error() &
Line
Count
Source
3940
1.59k
  TL_EXPECTED_11_CONSTEXPR E &error() & {
3941
1.59k
    TL_ASSERT(!has_value());
3942
1.59k
    return err().value();
3943
1.59k
  }
3944
  constexpr const E &&error() const && {
3945
    TL_ASSERT(!has_value());
3946
    return std::move(err().value());
3947
  }
3948
  TL_EXPECTED_11_CONSTEXPR E &&error() && {
3949
    TL_ASSERT(!has_value());
3950
    return std::move(err().value());
3951
  }
3952
3953
  template <class U>
3954
  constexpr T value_or(U &&v) const & {
3955
    static_assert(std::is_copy_constructible<T>::value &&
3956
                      std::is_convertible<U &&, T>::value,
3957
                  "T must be copy-constructible and convertible to from U&&");
3958
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
3959
  }
3960
  template <class U>
3961
  TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && {
3962
    static_assert(std::is_move_constructible<T>::value &&
3963
                      std::is_convertible<U &&, T>::value,
3964
                  "T must be move-constructible and convertible to from U&&");
3965
    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
3966
  }
3967
};
3968
3969
namespace detail {
3970
template <class Exp>
3971
using exp_t = typename detail::decay_t<Exp>::value_type;
3972
template <class Exp>
3973
using err_t = typename detail::decay_t<Exp>::error_type;
3974
template <class Exp, class Ret>
3975
using ret_t = expected<Ret, err_t<Exp>>;
3976
3977
#ifdef TL_EXPECTED_CXX14
3978
template <class Exp, class F,
3979
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
3980
          class Ret = decltype(detail::invoke(std::declval<F>(),
3981
                                              *std::declval<Exp>()))>
3982
constexpr auto and_then_impl(Exp &&exp, F &&f) {
3983
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3984
3985
  return exp.has_value()
3986
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
3987
             : Ret(unexpect, std::forward<Exp>(exp).error());
3988
}
3989
3990
template <class Exp, class F,
3991
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
3992
          class Ret = decltype(detail::invoke(std::declval<F>()))>
3993
constexpr auto and_then_impl(Exp &&exp, F &&f) {
3994
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3995
3996
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
3997
                         : Ret(unexpect, std::forward<Exp>(exp).error());
3998
}
3999
#else
4000
template <class>
4001
struct TC;
4002
template <class Exp, class F,
4003
          class Ret = decltype(detail::invoke(std::declval<F>(),
4004
                                              *std::declval<Exp>())),
4005
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr>
4006
auto and_then_impl(Exp &&exp, F &&f) -> Ret {
4007
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4008
4009
  return exp.has_value()
4010
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4011
             : Ret(unexpect, std::forward<Exp>(exp).error());
4012
}
4013
4014
template <class Exp, class F,
4015
          class Ret = decltype(detail::invoke(std::declval<F>())),
4016
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr>
4017
constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret {
4018
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4019
4020
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4021
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4022
}
4023
#endif
4024
4025
#ifdef TL_EXPECTED_CXX14
4026
template <class Exp, class F,
4027
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4028
          class Ret = decltype(detail::invoke(std::declval<F>(),
4029
                                              *std::declval<Exp>())),
4030
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4031
constexpr auto expected_map_impl(Exp &&exp, F &&f) {
4032
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4033
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4034
                                                 *std::forward<Exp>(exp)))
4035
                         : result(unexpect, std::forward<Exp>(exp).error());
4036
}
4037
4038
template <class Exp, class F,
4039
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4040
          class Ret = decltype(detail::invoke(std::declval<F>(),
4041
                                              *std::declval<Exp>())),
4042
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4043
auto expected_map_impl(Exp &&exp, F &&f) {
4044
  using result = expected<void, err_t<Exp>>;
4045
  if (exp.has_value()) {
4046
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4047
    return result();
4048
  }
4049
4050
  return result(unexpect, std::forward<Exp>(exp).error());
4051
}
4052
4053
template <class Exp, class F,
4054
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4055
          class Ret = decltype(detail::invoke(std::declval<F>())),
4056
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4057
constexpr auto expected_map_impl(Exp &&exp, F &&f) {
4058
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4059
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4060
                         : result(unexpect, std::forward<Exp>(exp).error());
4061
}
4062
4063
template <class Exp, class F,
4064
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4065
          class Ret = decltype(detail::invoke(std::declval<F>())),
4066
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4067
auto expected_map_impl(Exp &&exp, F &&f) {
4068
  using result = expected<void, err_t<Exp>>;
4069
  if (exp.has_value()) {
4070
    detail::invoke(std::forward<F>(f));
4071
    return result();
4072
  }
4073
4074
  return result(unexpect, std::forward<Exp>(exp).error());
4075
}
4076
#else
4077
template <class Exp, class F,
4078
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4079
          class Ret = decltype(detail::invoke(std::declval<F>(),
4080
                                              *std::declval<Exp>())),
4081
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4082
4083
constexpr auto expected_map_impl(Exp &&exp, F &&f)
4084
    -> ret_t<Exp, detail::decay_t<Ret>> {
4085
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4086
4087
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4088
                                                 *std::forward<Exp>(exp)))
4089
                         : result(unexpect, std::forward<Exp>(exp).error());
4090
}
4091
4092
template <class Exp, class F,
4093
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4094
          class Ret = decltype(detail::invoke(std::declval<F>(),
4095
                                              *std::declval<Exp>())),
4096
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4097
4098
auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
4099
  if (exp.has_value()) {
4100
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4101
    return {};
4102
  }
4103
4104
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4105
}
4106
4107
template <class Exp, class F,
4108
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4109
          class Ret = decltype(detail::invoke(std::declval<F>())),
4110
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4111
4112
constexpr auto expected_map_impl(Exp &&exp, F &&f)
4113
    -> ret_t<Exp, detail::decay_t<Ret>> {
4114
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4115
4116
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4117
                         : result(unexpect, std::forward<Exp>(exp).error());
4118
}
4119
4120
template <class Exp, class F,
4121
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4122
          class Ret = decltype(detail::invoke(std::declval<F>())),
4123
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4124
4125
auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
4126
  if (exp.has_value()) {
4127
    detail::invoke(std::forward<F>(f));
4128
    return {};
4129
  }
4130
4131
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4132
}
4133
#endif
4134
4135
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4136
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4137
template <class Exp, class F,
4138
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4139
          class Ret = decltype(detail::invoke(std::declval<F>(),
4140
                                              std::declval<Exp>().error())),
4141
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4142
constexpr auto map_error_impl(Exp &&exp, F &&f) {
4143
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4144
  return exp.has_value()
4145
             ? result(*std::forward<Exp>(exp))
4146
             : result(unexpect, detail::invoke(std::forward<F>(f),
4147
                                               std::forward<Exp>(exp).error()));
4148
}
4149
template <class Exp, class F,
4150
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4151
          class Ret = decltype(detail::invoke(std::declval<F>(),
4152
                                              std::declval<Exp>().error())),
4153
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4154
auto map_error_impl(Exp &&exp, F &&f) {
4155
  using result = expected<exp_t<Exp>, monostate>;
4156
  if (exp.has_value()) {
4157
    return result(*std::forward<Exp>(exp));
4158
  }
4159
4160
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4161
  return result(unexpect, monostate{});
4162
}
4163
template <class Exp, class F,
4164
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4165
          class Ret = decltype(detail::invoke(std::declval<F>(),
4166
                                              std::declval<Exp>().error())),
4167
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4168
constexpr auto map_error_impl(Exp &&exp, F &&f) {
4169
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4170
  return exp.has_value()
4171
             ? result()
4172
             : result(unexpect, detail::invoke(std::forward<F>(f),
4173
                                               std::forward<Exp>(exp).error()));
4174
}
4175
template <class Exp, class F,
4176
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4177
          class Ret = decltype(detail::invoke(std::declval<F>(),
4178
                                              std::declval<Exp>().error())),
4179
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4180
auto map_error_impl(Exp &&exp, F &&f) {
4181
  using result = expected<exp_t<Exp>, monostate>;
4182
  if (exp.has_value()) {
4183
    return result();
4184
  }
4185
4186
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4187
  return result(unexpect, monostate{});
4188
}
4189
#else
4190
template <class Exp, class F,
4191
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4192
          class Ret = decltype(detail::invoke(std::declval<F>(),
4193
                                              std::declval<Exp>().error())),
4194
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4195
constexpr auto map_error_impl(Exp &&exp, F &&f)
4196
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4197
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4198
4199
  return exp.has_value()
4200
             ? result(*std::forward<Exp>(exp))
4201
             : result(unexpect, detail::invoke(std::forward<F>(f),
4202
                                               std::forward<Exp>(exp).error()));
4203
}
4204
4205
template <class Exp, class F,
4206
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4207
          class Ret = decltype(detail::invoke(std::declval<F>(),
4208
                                              std::declval<Exp>().error())),
4209
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4210
auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
4211
  using result = expected<exp_t<Exp>, monostate>;
4212
  if (exp.has_value()) {
4213
    return result(*std::forward<Exp>(exp));
4214
  }
4215
4216
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4217
  return result(unexpect, monostate{});
4218
}
4219
4220
template <class Exp, class F,
4221
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4222
          class Ret = decltype(detail::invoke(std::declval<F>(),
4223
                                              std::declval<Exp>().error())),
4224
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4225
constexpr auto map_error_impl(Exp &&exp, F &&f)
4226
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4227
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4228
4229
  return exp.has_value()
4230
             ? result()
4231
             : result(unexpect, detail::invoke(std::forward<F>(f),
4232
                                               std::forward<Exp>(exp).error()));
4233
}
4234
4235
template <class Exp, class F,
4236
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4237
          class Ret = decltype(detail::invoke(std::declval<F>(),
4238
                                              std::declval<Exp>().error())),
4239
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4240
auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
4241
  using result = expected<exp_t<Exp>, monostate>;
4242
  if (exp.has_value()) {
4243
    return result();
4244
  }
4245
4246
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4247
  return result(unexpect, monostate{});
4248
}
4249
#endif
4250
4251
#ifdef TL_EXPECTED_CXX14
4252
template <class Exp, class F,
4253
          class Ret = decltype(detail::invoke(std::declval<F>(),
4254
                                              std::declval<Exp>().error())),
4255
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4256
constexpr auto or_else_impl(Exp &&exp, F &&f) {
4257
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4258
  return exp.has_value() ? std::forward<Exp>(exp)
4259
                         : detail::invoke(std::forward<F>(f),
4260
                                          std::forward<Exp>(exp).error());
4261
}
4262
4263
template <class Exp, class F,
4264
          class Ret = decltype(detail::invoke(std::declval<F>(),
4265
                                              std::declval<Exp>().error())),
4266
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4267
detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
4268
  return exp.has_value() ? std::forward<Exp>(exp)
4269
                         : (detail::invoke(std::forward<F>(f),
4270
                                           std::forward<Exp>(exp).error()),
4271
                            std::forward<Exp>(exp));
4272
}
4273
#else
4274
template <class Exp, class F,
4275
          class Ret = decltype(detail::invoke(std::declval<F>(),
4276
                                              std::declval<Exp>().error())),
4277
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4278
auto or_else_impl(Exp &&exp, F &&f) -> Ret {
4279
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4280
  return exp.has_value() ? std::forward<Exp>(exp)
4281
                         : detail::invoke(std::forward<F>(f),
4282
                                          std::forward<Exp>(exp).error());
4283
}
4284
4285
template <class Exp, class F,
4286
          class Ret = decltype(detail::invoke(std::declval<F>(),
4287
                                              std::declval<Exp>().error())),
4288
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4289
detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
4290
  return exp.has_value() ? std::forward<Exp>(exp)
4291
                         : (detail::invoke(std::forward<F>(f),
4292
                                           std::forward<Exp>(exp).error()),
4293
                            std::forward<Exp>(exp));
4294
}
4295
#endif
4296
}  // namespace detail
4297
4298
template <class T, class E, class U, class F>
4299
constexpr bool operator==(const expected<T, E> &lhs,
4300
                          const expected<U, F> &rhs) {
4301
  return (lhs.has_value() != rhs.has_value())
4302
             ? false
4303
             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
4304
}
4305
template <class T, class E, class U, class F>
4306
constexpr bool operator!=(const expected<T, E> &lhs,
4307
                          const expected<U, F> &rhs) {
4308
  return (lhs.has_value() != rhs.has_value())
4309
             ? true
4310
             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4311
}
4312
template <class E, class F>
4313
constexpr bool operator==(const expected<void, E> &lhs,
4314
                          const expected<void, F> &rhs) {
4315
  return (lhs.has_value() != rhs.has_value())
4316
             ? false
4317
             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
4318
}
4319
template <class E, class F>
4320
constexpr bool operator!=(const expected<void, E> &lhs,
4321
                          const expected<void, F> &rhs) {
4322
  return (lhs.has_value() != rhs.has_value())
4323
             ? true
4324
             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4325
}
4326
4327
template <class T, class E, class U>
4328
constexpr bool operator==(const expected<T, E> &x, const U &v) {
4329
  return x.has_value() ? *x == v : false;
4330
}
4331
template <class T, class E, class U>
4332
constexpr bool operator==(const U &v, const expected<T, E> &x) {
4333
  return x.has_value() ? *x == v : false;
4334
}
4335
template <class T, class E, class U>
4336
constexpr bool operator!=(const expected<T, E> &x, const U &v) {
4337
  return x.has_value() ? *x != v : true;
4338
}
4339
template <class T, class E, class U>
4340
constexpr bool operator!=(const U &v, const expected<T, E> &x) {
4341
  return x.has_value() ? *x != v : true;
4342
}
4343
4344
template <class T, class E>
4345
constexpr bool operator==(const expected<T, E> &x, const unexpected<E> &e) {
4346
  return x.has_value() ? false : x.error() == e.value();
4347
}
4348
template <class T, class E>
4349
constexpr bool operator==(const unexpected<E> &e, const expected<T, E> &x) {
4350
  return x.has_value() ? false : x.error() == e.value();
4351
}
4352
template <class T, class E>
4353
constexpr bool operator!=(const expected<T, E> &x, const unexpected<E> &e) {
4354
  return x.has_value() ? true : x.error() != e.value();
4355
}
4356
template <class T, class E>
4357
constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) {
4358
  return x.has_value() ? true : x.error() != e.value();
4359
}
4360
4361
template <class T, class E,
4362
          detail::enable_if_t<(std::is_void<T>::value ||
4363
                               std::is_move_constructible<T>::value) &&
4364
                              detail::is_swappable<T>::value &&
4365
                              std::is_move_constructible<E>::value &&
4366
                              detail::is_swappable<E>::value> * = nullptr>
4367
void swap(expected<T, E> &lhs,
4368
          expected<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
4369
  lhs.swap(rhs);
4370
}
4371
}  // namespace tl
4372
4373
#endif
4374
/* end file include/ada/expected.h */
4375
4376
/* begin file include/ada/url_pattern_regex.h */
4377
/**
4378
 * @file url_search_params.h
4379
 * @brief Declaration for the URL Search Params
4380
 */
4381
#ifndef ADA_URL_PATTERN_REGEX_H
4382
#define ADA_URL_PATTERN_REGEX_H
4383
4384
#include <string>
4385
#include <string_view>
4386
4387
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4388
#include <regex>
4389
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4390
4391
#if ADA_INCLUDE_URL_PATTERN
4392
namespace ada::url_pattern_regex {
4393
4394
template <typename T>
4395
concept regex_concept = requires(T t, std::string_view pattern,
4396
                                 bool ignore_case, std::string_view input) {
4397
  // Ensure the class has a type alias 'regex_type'
4398
  typename T::regex_type;
4399
4400
  // Function to create a regex instance
4401
  {
4402
    T::create_instance(pattern, ignore_case)
4403
  } -> std::same_as<std::optional<typename T::regex_type>>;
4404
4405
  // Function to perform regex search
4406
  {
4407
    T::regex_search(input, std::declval<typename T::regex_type&>())
4408
  } -> std::same_as<std::optional<std::vector<std::optional<std::string>>>>;
4409
4410
  // Function to match regex pattern
4411
  {
4412
    T::regex_match(input, std::declval<typename T::regex_type&>())
4413
  } -> std::same_as<bool>;
4414
4415
  // Copy constructor
4416
  { T(std::declval<const T&>()) } -> std::same_as<T>;
4417
4418
  // Move constructor
4419
  { T(std::declval<T&&>()) } -> std::same_as<T>;
4420
};
4421
4422
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4423
class std_regex_provider final {
4424
 public:
4425
  std_regex_provider() = default;
4426
  using regex_type = std::regex;
4427
  static std::optional<regex_type> create_instance(std::string_view pattern,
4428
                                                   bool ignore_case);
4429
  static std::optional<std::vector<std::optional<std::string>>> regex_search(
4430
      std::string_view input, const regex_type& pattern);
4431
  static bool regex_match(std::string_view input, const regex_type& pattern);
4432
};
4433
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4434
4435
}  // namespace ada::url_pattern_regex
4436
#endif  // ADA_INCLUDE_URL_PATTERN
4437
#endif  // ADA_URL_PATTERN_REGEX_H
4438
/* end file include/ada/url_pattern_regex.h */
4439
/* begin file include/ada/url_pattern_init.h */
4440
/**
4441
 * @file url_pattern_init.h
4442
 * @brief Declaration for the url_pattern_init implementation.
4443
 */
4444
#ifndef ADA_URL_PATTERN_INIT_H
4445
#define ADA_URL_PATTERN_INIT_H
4446
4447
/* begin file include/ada/errors.h */
4448
/**
4449
 * @file errors.h
4450
 * @brief Error type definitions for URL parsing.
4451
 *
4452
 * Defines the error codes that can be returned when URL parsing fails.
4453
 */
4454
#ifndef ADA_ERRORS_H
4455
#define ADA_ERRORS_H
4456
4457
#include <cstdint>
4458
namespace ada {
4459
/**
4460
 * @brief Error codes for URL parsing operations.
4461
 *
4462
 * Used with `tl::expected` to indicate why a URL parsing operation failed.
4463
 */
4464
enum class errors : uint8_t {
4465
  type_error /**< A type error occurred (e.g., invalid URL syntax). */
4466
};
4467
}  // namespace ada
4468
#endif  // ADA_ERRORS_H
4469
/* end file include/ada/errors.h */
4470
4471
#include <string_view>
4472
#include <string>
4473
#include <optional>
4474
#include <iostream>
4475
4476
#if ADA_TESTING
4477
#include <iostream>
4478
#endif  // ADA_TESTING
4479
4480
#if ADA_INCLUDE_URL_PATTERN
4481
namespace ada {
4482
4483
// Important: C++20 allows us to use concept rather than `using` or `typedef
4484
// and allows functions with second argument, which is optional (using either
4485
// std::nullopt or a parameter with default value)
4486
template <typename F>
4487
concept url_pattern_encoding_callback = requires(F f, std::string_view sv) {
4488
  { f(sv) } -> std::same_as<tl::expected<std::string, errors>>;
4489
};
4490
4491
// A structure providing matching patterns for individual components
4492
// of a URL. When a URLPattern is created, or when a URLPattern is
4493
// used to match or test against a URL, the input can be given as
4494
// either a string or a URLPatternInit struct. If a string is given,
4495
// it will be parsed to create a URLPatternInit. The URLPatternInit
4496
// API is defined as part of the URLPattern specification.
4497
// All provided strings must be valid UTF-8.
4498
struct url_pattern_init {
4499
  enum class process_type : uint8_t {
4500
    url,
4501
    pattern,
4502
  };
4503
4504
0
  friend std::ostream& operator<<(std::ostream& os, process_type type) {
4505
0
    switch (type) {
4506
0
      case process_type::url:
4507
0
        return os << "url";
4508
0
      case process_type::pattern:
4509
0
        return os << "pattern";
4510
0
      default:
4511
0
        return os << "unknown";
4512
0
    }
4513
0
  }
4514
4515
  // All strings must be valid UTF-8.
4516
  // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
4517
  static tl::expected<url_pattern_init, errors> process(
4518
      const url_pattern_init& init, process_type type,
4519
      std::optional<std::string_view> protocol = std::nullopt,
4520
      std::optional<std::string_view> username = std::nullopt,
4521
      std::optional<std::string_view> password = std::nullopt,
4522
      std::optional<std::string_view> hostname = std::nullopt,
4523
      std::optional<std::string_view> port = std::nullopt,
4524
      std::optional<std::string_view> pathname = std::nullopt,
4525
      std::optional<std::string_view> search = std::nullopt,
4526
      std::optional<std::string_view> hash = std::nullopt);
4527
4528
  // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init
4529
  static tl::expected<std::string, errors> process_protocol(
4530
      std::string_view value, process_type type);
4531
4532
  // @see https://urlpattern.spec.whatwg.org/#process-username-for-init
4533
  static tl::expected<std::string, errors> process_username(
4534
      std::string_view value, process_type type);
4535
4536
  // @see https://urlpattern.spec.whatwg.org/#process-password-for-init
4537
  static tl::expected<std::string, errors> process_password(
4538
      std::string_view value, process_type type);
4539
4540
  // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init
4541
  static tl::expected<std::string, errors> process_hostname(
4542
      std::string_view value, process_type type);
4543
4544
  // @see https://urlpattern.spec.whatwg.org/#process-port-for-init
4545
  static tl::expected<std::string, errors> process_port(
4546
      std::string_view port, std::string_view protocol, process_type type);
4547
4548
  // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init
4549
  static tl::expected<std::string, errors> process_pathname(
4550
      std::string_view value, std::string_view protocol, process_type type);
4551
4552
  // @see https://urlpattern.spec.whatwg.org/#process-search-for-init
4553
  static tl::expected<std::string, errors> process_search(
4554
      std::string_view value, process_type type);
4555
4556
  // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init
4557
  static tl::expected<std::string, errors> process_hash(std::string_view value,
4558
                                                        process_type type);
4559
4560
#if ADA_TESTING
4561
  friend void PrintTo(const url_pattern_init& init, std::ostream* os) {
4562
    *os << "protocol: '" << init.protocol.value_or("undefined") << "', ";
4563
    *os << "username: '" << init.username.value_or("undefined") << "', ";
4564
    *os << "password: '" << init.password.value_or("undefined") << "', ";
4565
    *os << "hostname: '" << init.hostname.value_or("undefined") << "', ";
4566
    *os << "port: '" << init.port.value_or("undefined") << "', ";
4567
    *os << "pathname: '" << init.pathname.value_or("undefined") << "', ";
4568
    *os << "search: '" << init.search.value_or("undefined") << "', ";
4569
    *os << "hash: '" << init.hash.value_or("undefined") << "', ";
4570
    *os << "base_url: '" << init.base_url.value_or("undefined") << "', ";
4571
  }
4572
#endif  // ADA_TESTING
4573
4574
  bool operator==(const url_pattern_init&) const;
4575
  // If present, must be valid UTF-8.
4576
  std::optional<std::string> protocol{};
4577
  // If present, must be valid UTF-8.
4578
  std::optional<std::string> username{};
4579
  // If present, must be valid UTF-8.
4580
  std::optional<std::string> password{};
4581
  // If present, must be valid UTF-8.
4582
  std::optional<std::string> hostname{};
4583
  // If present, must be valid UTF-8.
4584
  std::optional<std::string> port{};
4585
  // If present, must be valid UTF-8.
4586
  std::optional<std::string> pathname{};
4587
  // If present, must be valid UTF-8.
4588
  std::optional<std::string> search{};
4589
  // If present, must be valid UTF-8.
4590
  std::optional<std::string> hash{};
4591
  // If present, must be valid UTF-8.
4592
  std::optional<std::string> base_url{};
4593
};
4594
}  // namespace ada
4595
#endif  // ADA_INCLUDE_URL_PATTERN
4596
#endif  // ADA_URL_PATTERN_INIT_H
4597
/* end file include/ada/url_pattern_init.h */
4598
4599
/** @private Forward declarations */
4600
namespace ada {
4601
struct url_aggregator;
4602
struct url;
4603
#if ADA_INCLUDE_URL_PATTERN
4604
template <url_pattern_regex::regex_concept regex_provider>
4605
class url_pattern;
4606
struct url_pattern_options;
4607
#endif  // ADA_INCLUDE_URL_PATTERN
4608
enum class errors : uint8_t;
4609
}  // namespace ada
4610
4611
/**
4612
 * @namespace ada::parser
4613
 * @brief Internal URL parsing implementation.
4614
 *
4615
 * Contains the core URL parsing algorithm as specified by the WHATWG URL
4616
 * Standard. These functions are used internally by `ada::parse()`.
4617
 */
4618
namespace ada::parser {
4619
/**
4620
 * Parses a URL string into a URL object.
4621
 *
4622
 * @tparam result_type The type of URL object to create (url or url_aggregator).
4623
 *
4624
 * @param user_input The URL string to parse (must be valid UTF-8).
4625
 * @param base_url Optional base URL for resolving relative URLs.
4626
 *
4627
 * @return The parsed URL object. Check `is_valid` to determine if parsing
4628
 *         succeeded.
4629
 *
4630
 * @see https://url.spec.whatwg.org/#concept-basic-url-parser
4631
 */
4632
template <typename result_type = url_aggregator>
4633
result_type parse_url(std::string_view user_input,
4634
                      const result_type* base_url = nullptr);
4635
4636
extern template url_aggregator parse_url<url_aggregator>(
4637
    std::string_view user_input, const url_aggregator* base_url);
4638
extern template url parse_url<url>(std::string_view user_input,
4639
                                   const url* base_url);
4640
4641
template <typename result_type = url_aggregator, bool store_values = true>
4642
result_type parse_url_impl(std::string_view user_input,
4643
                           const result_type* base_url = nullptr);
4644
4645
extern template url_aggregator parse_url_impl<url_aggregator>(
4646
    std::string_view user_input, const url_aggregator* base_url);
4647
extern template url parse_url_impl<url>(std::string_view user_input,
4648
                                        const url* base_url);
4649
4650
#if ADA_INCLUDE_URL_PATTERN
4651
template <url_pattern_regex::regex_concept regex_provider>
4652
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
4653
    std::variant<std::string_view, url_pattern_init>&& input,
4654
    const std::string_view* base_url, const url_pattern_options* options);
4655
#endif  // ADA_INCLUDE_URL_PATTERN
4656
4657
}  // namespace ada::parser
4658
4659
#endif  // ADA_PARSER_H
4660
/* end file include/ada/parser.h */
4661
/* begin file include/ada/parser-inl.h */
4662
/**
4663
 * @file parser-inl.h
4664
 */
4665
#ifndef ADA_PARSER_INL_H
4666
#define ADA_PARSER_INL_H
4667
4668
/* begin file include/ada/url_pattern.h */
4669
/**
4670
 * @file url_pattern.h
4671
 * @brief URLPattern API implementation.
4672
 *
4673
 * This header provides the URLPattern API as specified by the WHATWG URL
4674
 * Pattern Standard. URLPattern allows matching URLs against patterns with
4675
 * wildcards and named groups, similar to how regular expressions match strings.
4676
 *
4677
 * @see https://urlpattern.spec.whatwg.org/
4678
 * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
4679
 */
4680
#ifndef ADA_URL_PATTERN_H
4681
#define ADA_URL_PATTERN_H
4682
4683
/* begin file include/ada/implementation.h */
4684
/**
4685
 * @file implementation.h
4686
 * @brief User-facing functions for URL parsing and manipulation.
4687
 *
4688
 * This header provides the primary public API for parsing URLs in Ada.
4689
 * It includes the main `ada::parse()` function which is the recommended
4690
 * entry point for most users.
4691
 *
4692
 * @see https://url.spec.whatwg.org/#api
4693
 */
4694
#ifndef ADA_IMPLEMENTATION_H
4695
#define ADA_IMPLEMENTATION_H
4696
4697
#include <string>
4698
#include <string_view>
4699
#include <optional>
4700
4701
/* begin file include/ada/url.h */
4702
/**
4703
 * @file url.h
4704
 * @brief Declaration for the `ada::url` class.
4705
 *
4706
 * This file contains the `ada::url` struct which represents a parsed URL
4707
 * using separate `std::string` instances for each component. This
4708
 * representation is more flexible but uses more memory than `url_aggregator`.
4709
 *
4710
 * @see url_aggregator.h for a more memory-efficient alternative
4711
 */
4712
#ifndef ADA_URL_H
4713
#define ADA_URL_H
4714
4715
#include <algorithm>
4716
#include <optional>
4717
#include <ostream>
4718
#include <string>
4719
#include <string_view>
4720
4721
/* begin file include/ada/url_components.h */
4722
/**
4723
 * @file url_components.h
4724
 * @brief URL component offset representation for url_aggregator.
4725
 *
4726
 * This file defines the `url_components` struct which stores byte offsets
4727
 * into a URL string buffer. It is used internally by `url_aggregator` to
4728
 * efficiently locate URL components without storing separate strings.
4729
 */
4730
#ifndef ADA_URL_COMPONENTS_H
4731
#define ADA_URL_COMPONENTS_H
4732
4733
namespace ada {
4734
4735
/**
4736
 * @brief Stores byte offsets for URL components within a buffer.
4737
 *
4738
 * The `url_components` struct uses 32-bit offsets to track the boundaries
4739
 * of each URL component within a single string buffer. This enables efficient
4740
 * component extraction without additional memory allocations.
4741
 *
4742
 * Component layout in a URL:
4743
 * ```
4744
 * https://user:pass@example.com:1234/foo/bar?baz#quux
4745
 *       |     |    |          | ^^^^|       |   |
4746
 *       |     |    |          | |   |       |   `----- hash_start
4747
 *       |     |    |          | |   |       `--------- search_start
4748
 *       |     |    |          | |   `----------------- pathname_start
4749
 *       |     |    |          | `--------------------- port
4750
 *       |     |    |          `----------------------- host_end
4751
 *       |     |    `---------------------------------- host_start
4752
 *       |     `--------------------------------------- username_end
4753
 *       `--------------------------------------------- protocol_end
4754
 * ```
4755
 *
4756
 * @note The 32-bit offsets limit URLs to 4GB in length.
4757
 * @note A value of `omitted` (UINT32_MAX) indicates the component is not
4758
 * present.
4759
 */
4760
struct url_components {
4761
  /** Sentinel value indicating a component is not present. */
4762
  constexpr static uint32_t omitted = uint32_t(-1);
4763
4764
4.42k
  url_components() = default;
4765
  url_components(const url_components &u) = default;
4766
  url_components(url_components &&u) noexcept = default;
4767
  url_components &operator=(url_components &&u) noexcept = default;
4768
  url_components &operator=(const url_components &u) = default;
4769
  ~url_components() = default;
4770
4771
  /** Offset of the end of the protocol/scheme (position of ':'). */
4772
  uint32_t protocol_end{0};
4773
4774
  /**
4775
   * Offset of the end of the username.
4776
   * Initialized to 0 (not `omitted`) to simplify username/password getters.
4777
   */
4778
  uint32_t username_end{0};
4779
4780
  /** Offset of the start of the host. */
4781
  uint32_t host_start{0};
4782
4783
  /** Offset of the end of the host. */
4784
  uint32_t host_end{0};
4785
4786
  /** Port number, or `omitted` if no port is specified. */
4787
  uint32_t port{omitted};
4788
4789
  /** Offset of the start of the pathname. */
4790
  uint32_t pathname_start{0};
4791
4792
  /** Offset of the '?' starting the query, or `omitted` if no query. */
4793
  uint32_t search_start{omitted};
4794
4795
  /** Offset of the '#' starting the fragment, or `omitted` if no fragment. */
4796
  uint32_t hash_start{omitted};
4797
4798
  /**
4799
   * Validates that offsets are in ascending order and consistent.
4800
   * Useful for debugging to detect internal corruption.
4801
   * @return `true` if offsets are consistent, `false` otherwise.
4802
   */
4803
  [[nodiscard]] constexpr bool check_offset_consistency() const noexcept;
4804
4805
  /**
4806
   * Returns a JSON string representation of the offsets for debugging.
4807
   * @return A JSON-formatted string with all offset values.
4808
   */
4809
  [[nodiscard]] std::string to_string() const;
4810
4811
};  // struct url_components
4812
}  // namespace ada
4813
#endif
4814
/* end file include/ada/url_components.h */
4815
4816
namespace ada {
4817
4818
struct url_aggregator;
4819
4820
// namespace parser {
4821
// template <typename result_type>
4822
// result_type parse_url(std::string_view user_input,
4823
//                       const result_type* base_url = nullptr);
4824
// template <typename result_type, bool store_values>
4825
// result_type parse_url_impl(std::string_view user_input,
4826
//                            const result_type* base_url = nullptr);
4827
// }
4828
4829
/**
4830
 * @brief Represents a parsed URL with individual string components.
4831
 *
4832
 * The `url` struct stores each URL component (scheme, username, password,
4833
 * host, port, path, query, fragment) as a separate `std::string`. This
4834
 * provides flexibility but incurs more memory allocations compared to
4835
 * `url_aggregator`.
4836
 *
4837
 * **When to use `ada::url`:**
4838
 * - When you need to frequently modify individual URL components
4839
 * - When you want independent ownership of component strings
4840
 *
4841
 * **When to use `ada::url_aggregator` instead:**
4842
 * - For read-mostly operations on parsed URLs
4843
 * - When memory efficiency is important
4844
 * - When you only need string_view access to components
4845
 *
4846
 * @note This type is returned when parsing with `ada::parse<ada::url>()`.
4847
 *       By default, `ada::parse()` returns `ada::url_aggregator`.
4848
 *
4849
 * @see url_aggregator For a more memory-efficient URL representation
4850
 * @see https://url.spec.whatwg.org/#url-representation
4851
 */
4852
struct url : url_base {
4853
0
  url() = default;
4854
  url(const url &u) = default;
4855
0
  url(url &&u) noexcept = default;
4856
  url &operator=(url &&u) noexcept = default;
4857
0
  url &operator=(const url &u) = default;
4858
0
  ~url() override = default;
4859
4860
  /**
4861
   * @private
4862
   * A URL's username is an ASCII string identifying a username. It is initially
4863
   * the empty string.
4864
   */
4865
  std::string username{};
4866
4867
  /**
4868
   * @private
4869
   * A URL's password is an ASCII string identifying a password. It is initially
4870
   * the empty string.
4871
   */
4872
  std::string password{};
4873
4874
  /**
4875
   * @private
4876
   * A URL's host is null or a host. It is initially null.
4877
   */
4878
  std::optional<std::string> host{};
4879
4880
  /**
4881
   * @private
4882
   * A URL's port is either null or a 16-bit unsigned integer that identifies a
4883
   * networking port. It is initially null.
4884
   */
4885
  std::optional<uint16_t> port{};
4886
4887
  /**
4888
   * @private
4889
   * A URL's path is either an ASCII string or a list of zero or more ASCII
4890
   * strings, usually identifying a location.
4891
   */
4892
  std::string path{};
4893
4894
  /**
4895
   * @private
4896
   * A URL's query is either null or an ASCII string. It is initially null.
4897
   */
4898
  std::optional<std::string> query{};
4899
4900
  /**
4901
   * @private
4902
   * A URL's fragment is either null or an ASCII string that can be used for
4903
   * further processing on the resource the URL's other components identify. It
4904
   * is initially null.
4905
   */
4906
  std::optional<std::string> hash{};
4907
4908
  /**
4909
   * Checks if the URL has an empty hostname (host is set but empty string).
4910
   * @return `true` if host exists but is empty, `false` otherwise.
4911
   */
4912
  [[nodiscard]] inline bool has_empty_hostname() const noexcept;
4913
4914
  /**
4915
   * Checks if the URL has a non-default port explicitly specified.
4916
   * @return `true` if a port is present, `false` otherwise.
4917
   */
4918
  [[nodiscard]] inline bool has_port() const noexcept;
4919
4920
  /**
4921
   * Checks if the URL has a hostname (including empty hostnames).
4922
   * @return `true` if host is present, `false` otherwise.
4923
   */
4924
  [[nodiscard]] inline bool has_hostname() const noexcept;
4925
4926
  /**
4927
   * Validates whether the hostname is a valid domain according to RFC 1034.
4928
   * Checks that the domain and its labels have valid lengths (max 255 octets
4929
   * total, max 63 octets per label).
4930
   * @return `true` if the domain is valid, `false` otherwise.
4931
   */
4932
  [[nodiscard]] bool has_valid_domain() const noexcept override;
4933
4934
  /**
4935
   * Returns a JSON string representation of this URL for debugging.
4936
   * @return A JSON-formatted string with all URL components.
4937
   */
4938
  [[nodiscard]] std::string to_string() const override;
4939
4940
  /**
4941
   * Returns the full serialized URL (the href).
4942
   * @return The complete URL string (allocates a new string).
4943
   * @see https://url.spec.whatwg.org/#dom-url-href
4944
   */
4945
  [[nodiscard]] ada_really_inline std::string get_href() const noexcept;
4946
4947
  /**
4948
   * Returns the URL's origin as a string (scheme + host + port for special
4949
   * URLs).
4950
   * @return A newly allocated string containing the serialized origin.
4951
   * @see https://url.spec.whatwg.org/#concept-url-origin
4952
   */
4953
  [[nodiscard]] std::string get_origin() const noexcept override;
4954
4955
  /**
4956
   * Returns the URL's scheme followed by a colon (e.g., "https:").
4957
   * @return A newly allocated string with the protocol.
4958
   * @see https://url.spec.whatwg.org/#dom-url-protocol
4959
   */
4960
  [[nodiscard]] std::string get_protocol() const noexcept;
4961
4962
  /**
4963
   * Returns the URL's host and port (e.g., "example.com:8080").
4964
   * If no port is set, returns just the host. Returns empty string if no host.
4965
   * @return A newly allocated string with host:port.
4966
   * @see https://url.spec.whatwg.org/#dom-url-host
4967
   */
4968
  [[nodiscard]] std::string get_host() const noexcept;
4969
4970
  /**
4971
   * Returns the URL's hostname (without port).
4972
   * Returns empty string if no host is set.
4973
   * @return A newly allocated string with the hostname.
4974
   * @see https://url.spec.whatwg.org/#dom-url-hostname
4975
   */
4976
  [[nodiscard]] std::string get_hostname() const noexcept;
4977
4978
  /**
4979
   * Returns the URL's path component.
4980
   * @return A string_view pointing to the path.
4981
   * @see https://url.spec.whatwg.org/#dom-url-pathname
4982
   */
4983
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept;
4984
4985
  /**
4986
   * Returns the byte length of the pathname without creating a string.
4987
   * @return Size of the pathname in bytes.
4988
   * @see https://url.spec.whatwg.org/#dom-url-pathname
4989
   */
4990
  [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
4991
4992
  /**
4993
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
4994
   * Returns empty string if no query is set.
4995
   * @return A newly allocated string with the search/query.
4996
   * @see https://url.spec.whatwg.org/#dom-url-search
4997
   */
4998
  [[nodiscard]] std::string get_search() const noexcept;
4999
5000
  /**
5001
   * Returns the URL's username component.
5002
   * @return A constant reference to the username string.
5003
   * @see https://url.spec.whatwg.org/#dom-url-username
5004
   */
5005
  [[nodiscard]] const std::string &get_username() const noexcept;
5006
5007
  /**
5008
   * Sets the URL's username, percent-encoding special characters.
5009
   * @param input The new username value.
5010
   * @return `true` on success, `false` if the URL cannot have credentials.
5011
   * @see https://url.spec.whatwg.org/#dom-url-username
5012
   */
5013
  bool set_username(std::string_view input);
5014
5015
  /**
5016
   * Sets the URL's password, percent-encoding special characters.
5017
   * @param input The new password value.
5018
   * @return `true` on success, `false` if the URL cannot have credentials.
5019
   * @see https://url.spec.whatwg.org/#dom-url-password
5020
   */
5021
  bool set_password(std::string_view input);
5022
5023
  /**
5024
   * Sets the URL's port from a string (e.g., "8080").
5025
   * @param input The port string. Empty string removes the port.
5026
   * @return `true` on success, `false` if the URL cannot have a port.
5027
   * @see https://url.spec.whatwg.org/#dom-url-port
5028
   */
5029
  bool set_port(std::string_view input);
5030
5031
  /**
5032
   * Sets the URL's fragment/hash (the part after '#').
5033
   * @param input The new hash value (with or without leading '#').
5034
   * @see https://url.spec.whatwg.org/#dom-url-hash
5035
   */
5036
  void set_hash(std::string_view input);
5037
5038
  /**
5039
   * Sets the URL's query string (the part after '?').
5040
   * @param input The new query value (with or without leading '?').
5041
   * @see https://url.spec.whatwg.org/#dom-url-search
5042
   */
5043
  void set_search(std::string_view input);
5044
5045
  /**
5046
   * Sets the URL's pathname.
5047
   * @param input The new path value.
5048
   * @return `true` on success, `false` if the URL has an opaque path.
5049
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5050
   */
5051
  bool set_pathname(std::string_view input);
5052
5053
  /**
5054
   * Sets the URL's host (hostname and optionally port).
5055
   * @param input The new host value (e.g., "example.com:8080").
5056
   * @return `true` on success, `false` if parsing fails.
5057
   * @see https://url.spec.whatwg.org/#dom-url-host
5058
   */
5059
  bool set_host(std::string_view input);
5060
5061
  /**
5062
   * Sets the URL's hostname (without port).
5063
   * @param input The new hostname value.
5064
   * @return `true` on success, `false` if parsing fails.
5065
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5066
   */
5067
  bool set_hostname(std::string_view input);
5068
5069
  /**
5070
   * Sets the URL's protocol/scheme.
5071
   * @param input The new protocol (with or without trailing ':').
5072
   * @return `true` on success, `false` if the scheme is invalid.
5073
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5074
   */
5075
  bool set_protocol(std::string_view input);
5076
5077
  /**
5078
   * Replaces the entire URL by parsing a new href string.
5079
   * @param input The new URL string to parse.
5080
   * @return `true` on success, `false` if parsing fails.
5081
   * @see https://url.spec.whatwg.org/#dom-url-href
5082
   */
5083
  bool set_href(std::string_view input);
5084
5085
  /**
5086
   * Returns the URL's password component.
5087
   * @return A constant reference to the password string.
5088
   * @see https://url.spec.whatwg.org/#dom-url-password
5089
   */
5090
  [[nodiscard]] const std::string &get_password() const noexcept;
5091
5092
  /**
5093
   * Returns the URL's port as a string (e.g., "8080").
5094
   * Returns empty string if no port is set.
5095
   * @return A newly allocated string with the port.
5096
   * @see https://url.spec.whatwg.org/#dom-url-port
5097
   */
5098
  [[nodiscard]] std::string get_port() const noexcept;
5099
5100
  /**
5101
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
5102
   * Returns empty string if no fragment is set.
5103
   * @return A newly allocated string with the hash.
5104
   * @see https://url.spec.whatwg.org/#dom-url-hash
5105
   */
5106
  [[nodiscard]] std::string get_hash() const noexcept;
5107
5108
  /**
5109
   * Checks if the URL has credentials (non-empty username or password).
5110
   * @return `true` if username or password is non-empty, `false` otherwise.
5111
   */
5112
  [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5113
5114
  /**
5115
   * Returns the URL component offsets for efficient serialization.
5116
   *
5117
   * The components represent byte offsets into the serialized URL:
5118
   * ```
5119
   * https://user:pass@example.com:1234/foo/bar?baz#quux
5120
   *       |     |    |          | ^^^^|       |   |
5121
   *       |     |    |          | |   |       |   `----- hash_start
5122
   *       |     |    |          | |   |       `--------- search_start
5123
   *       |     |    |          | |   `----------------- pathname_start
5124
   *       |     |    |          | `--------------------- port
5125
   *       |     |    |          `----------------------- host_end
5126
   *       |     |    `---------------------------------- host_start
5127
   *       |     `--------------------------------------- username_end
5128
   *       `--------------------------------------------- protocol_end
5129
   * ```
5130
   * @return A newly constructed url_components struct.
5131
   * @see https://github.com/servo/rust-url
5132
   */
5133
  [[nodiscard]] ada_really_inline ada::url_components get_components()
5134
      const noexcept;
5135
5136
  /**
5137
   * Checks if the URL has a fragment/hash component.
5138
   * @return `true` if hash is present, `false` otherwise.
5139
   */
5140
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
5141
5142
  /**
5143
   * Checks if the URL has a query/search component.
5144
   * @return `true` if query is present, `false` otherwise.
5145
   */
5146
  [[nodiscard]] constexpr bool has_search() const noexcept override;
5147
5148
 private:
5149
  friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5150
                                                   const ada::url *);
5151
  friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5152
      std::string_view, const ada::url_aggregator *);
5153
  friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5154
      ada::url &url) noexcept;
5155
5156
  friend ada::url ada::parser::parse_url_impl<ada::url, true>(std::string_view,
5157
                                                              const ada::url *);
5158
  friend ada::url_aggregator ada::parser::parse_url_impl<
5159
      ada::url_aggregator, true>(std::string_view, const ada::url_aggregator *);
5160
5161
  inline void update_unencoded_base_hash(std::string_view input);
5162
  inline void update_base_hostname(std::string_view input);
5163
  inline void update_base_search(std::string_view input,
5164
                                 const uint8_t query_percent_encode_set[]);
5165
  inline void update_base_search(std::optional<std::string> &&input);
5166
  inline void update_base_pathname(std::string_view input);
5167
  inline void update_base_username(std::string_view input);
5168
  inline void update_base_password(std::string_view input);
5169
  inline void update_base_port(std::optional<uint16_t> input);
5170
5171
  /**
5172
   * Sets the host or hostname according to override condition.
5173
   * Return true on success.
5174
   * @see https://url.spec.whatwg.org/#hostname-state
5175
   */
5176
  template <bool override_hostname = false>
5177
  bool set_host_or_hostname(std::string_view input);
5178
5179
  /**
5180
   * Return true on success.
5181
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5182
   */
5183
  [[nodiscard]] bool parse_ipv4(std::string_view input);
5184
5185
  /**
5186
   * Return true on success.
5187
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5188
   */
5189
  [[nodiscard]] bool parse_ipv6(std::string_view input);
5190
5191
  /**
5192
   * Return true on success.
5193
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5194
   */
5195
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
5196
5197
  /**
5198
   * A URL's scheme is an ASCII string that identifies the type of URL and can
5199
   * be used to dispatch a URL for further processing after parsing. It is
5200
   * initially the empty string. We only set non_special_scheme when the scheme
5201
   * is non-special, otherwise we avoid constructing string.
5202
   *
5203
   * Special schemes are stored in ada::scheme::details::is_special_list so we
5204
   * typically do not need to store them in each url instance.
5205
   */
5206
  std::string non_special_scheme{};
5207
5208
  /**
5209
   * A URL cannot have a username/password/port if its host is null or the empty
5210
   * string, or its scheme is "file".
5211
   */
5212
  [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5213
5214
  ada_really_inline size_t parse_port(
5215
      std::string_view view, bool check_trailing_content) noexcept override;
5216
5217
0
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5218
0
    return this->parse_port(view, false);
5219
0
  }
5220
5221
  /**
5222
   * Parse the host from the provided input. We assume that
5223
   * the input does not contain spaces or tabs. Control
5224
   * characters and spaces are not trimmed (they should have
5225
   * been removed if needed).
5226
   * Return true on success.
5227
   * @see https://url.spec.whatwg.org/#host-parsing
5228
   */
5229
  [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5230
5231
  template <bool has_state_override = false>
5232
  [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5233
5234
  constexpr void clear_pathname() override;
5235
  constexpr void clear_search() override;
5236
  constexpr void set_protocol_as_file();
5237
5238
  /**
5239
   * Parse the path from the provided input.
5240
   * Return true on success. Control characters not
5241
   * trimmed from the ends (they should have
5242
   * been removed if needed).
5243
   *
5244
   * The input is expected to be UTF-8.
5245
   *
5246
   * @see https://url.spec.whatwg.org/
5247
   */
5248
  ada_really_inline void parse_path(std::string_view input);
5249
5250
  /**
5251
   * Set the scheme for this URL. The provided scheme should be a valid
5252
   * scheme string, be lower-cased, not contain spaces or tabs. It should
5253
   * have no spurious trailing or leading content.
5254
   */
5255
  inline void set_scheme(std::string &&new_scheme) noexcept;
5256
5257
  /**
5258
   * Take the scheme from another URL. The scheme string is moved from the
5259
   * provided url.
5260
   */
5261
  constexpr void copy_scheme(ada::url &&u) noexcept;
5262
5263
  /**
5264
   * Take the scheme from another URL. The scheme string is copied from the
5265
   * provided url.
5266
   */
5267
  constexpr void copy_scheme(const ada::url &u);
5268
5269
};  // struct url
5270
5271
inline std::ostream &operator<<(std::ostream &out, const ada::url &u);
5272
}  // namespace ada
5273
5274
#endif  // ADA_URL_H
5275
/* end file include/ada/url.h */
5276
5277
namespace ada {
5278
5279
/**
5280
 * Result type for URL parsing operations.
5281
 *
5282
 * Uses `tl::expected` to represent either a successfully parsed URL or an
5283
 * error. This allows for exception-free error handling.
5284
 *
5285
 * @tparam result_type The URL type to return (default: `ada::url_aggregator`)
5286
 *
5287
 * @example
5288
 * ```cpp
5289
 * ada::result<ada::url_aggregator> result = ada::parse("https://example.com");
5290
 * if (result) {
5291
 *     // Success: use result.value() or *result
5292
 * } else {
5293
 *     // Error: handle result.error()
5294
 * }
5295
 * ```
5296
 */
5297
template <class result_type = ada::url_aggregator>
5298
using result = tl::expected<result_type, ada::errors>;
5299
5300
/**
5301
 * Parses a URL string according to the WHATWG URL Standard.
5302
 *
5303
 * This is the main entry point for URL parsing in Ada. The function takes
5304
 * a string input and optionally a base URL for resolving relative URLs.
5305
 *
5306
 * @tparam result_type The URL type to return. Can be either `ada::url` or
5307
 *         `ada::url_aggregator` (default). The `url_aggregator` type is more
5308
 *         memory-efficient as it stores components as offsets into a single
5309
 *         buffer.
5310
 *
5311
 * @param input The URL string to parse. Must be valid ASCII or UTF-8 encoded.
5312
 *        Leading and trailing whitespace is automatically trimmed.
5313
 * @param base_url Optional pointer to a base URL for resolving relative URLs.
5314
 *        If nullptr (default), only absolute URLs can be parsed successfully.
5315
 *
5316
 * @return A `result<result_type>` containing either the parsed URL on success,
5317
 *         or an error code on failure. Use the boolean conversion or
5318
 *         `has_value()` to check for success.
5319
 *
5320
 * @note The parser is fully compliant with the WHATWG URL Standard.
5321
 *
5322
 * @example
5323
 * ```cpp
5324
 * // Parse an absolute URL
5325
 * auto url = ada::parse("https://user:pass@example.com:8080/path?query#hash");
5326
 * if (url) {
5327
 *     std::cout << url->get_hostname(); // "example.com"
5328
 *     std::cout << url->get_pathname(); // "/path"
5329
 * }
5330
 *
5331
 * // Parse a relative URL with a base
5332
 * auto base = ada::parse("https://example.com/dir/");
5333
 * if (base) {
5334
 *     auto relative = ada::parse("../other/page", &*base);
5335
 *     if (relative) {
5336
 *         std::cout << relative->get_href(); //
5337
 * "https://example.com/other/page"
5338
 *     }
5339
 * }
5340
 * ```
5341
 *
5342
 * @see https://url.spec.whatwg.org/#url-parsing
5343
 */
5344
template <class result_type = ada::url_aggregator>
5345
ada_warn_unused ada::result<result_type> parse(
5346
    std::string_view input, const result_type* base_url = nullptr);
5347
5348
extern template ada::result<url> parse<url>(std::string_view input,
5349
                                            const url* base_url);
5350
extern template ada::result<url_aggregator> parse<url_aggregator>(
5351
    std::string_view input, const url_aggregator* base_url);
5352
5353
/**
5354
 * Checks whether a URL string can be successfully parsed.
5355
 *
5356
 * This is a fast validation function that checks if a URL string is valid
5357
 * according to the WHATWG URL Standard without fully constructing a URL
5358
 * object. Use this when you only need to validate URLs without needing
5359
 * their parsed components.
5360
 *
5361
 * @param input The URL string to validate. Must be valid ASCII or UTF-8.
5362
 * @param base_input Optional pointer to a base URL string for resolving
5363
 *        relative URLs. If nullptr (default), the input is validated as
5364
 *        an absolute URL.
5365
 *
5366
 * @return `true` if the URL can be parsed successfully, `false` otherwise.
5367
 *
5368
 * @example
5369
 * ```cpp
5370
 * // Check absolute URL
5371
 * bool valid = ada::can_parse("https://example.com"); // true
5372
 * bool invalid = ada::can_parse("not a url");         // false
5373
 *
5374
 * // Check relative URL with base
5375
 * std::string_view base = "https://example.com/";
5376
 * bool relative_valid = ada::can_parse("../path", &base); // true
5377
 * ```
5378
 *
5379
 * @see https://url.spec.whatwg.org/#dom-url-canparse
5380
 */
5381
bool can_parse(std::string_view input,
5382
               const std::string_view* base_input = nullptr);
5383
5384
#if ADA_INCLUDE_URL_PATTERN
5385
/**
5386
 * Parses a URL pattern according to the URLPattern specification.
5387
 *
5388
 * URL patterns provide a syntax for matching URLs against patterns, similar
5389
 * to how regular expressions match strings. This is useful for routing and
5390
 * URL-based dispatching.
5391
 *
5392
 * @tparam regex_provider The regex implementation to use for pattern matching.
5393
 *
5394
 * @param input Either a URL pattern string (valid UTF-8) or a URLPatternInit
5395
 *        struct specifying individual component patterns.
5396
 * @param base_url Optional pointer to a base URL string (valid UTF-8) for
5397
 *        resolving relative patterns.
5398
 * @param options Optional pointer to configuration options (e.g., ignore_case).
5399
 *
5400
 * @return A `tl::expected` containing either the parsed url_pattern on success,
5401
 *         or an error code on failure.
5402
 *
5403
 * @see https://urlpattern.spec.whatwg.org
5404
 */
5405
template <url_pattern_regex::regex_concept regex_provider>
5406
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
5407
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
5408
                  const std::string_view* base_url = nullptr,
5409
                  const url_pattern_options* options = nullptr);
5410
#endif  // ADA_INCLUDE_URL_PATTERN
5411
5412
/**
5413
 * Converts a file system path to a file:// URL.
5414
 *
5415
 * Creates a properly formatted file URL from a local file system path.
5416
 * Handles platform-specific path separators and percent-encoding.
5417
 *
5418
 * @param path The file system path to convert. Must be valid ASCII or UTF-8.
5419
 *
5420
 * @return A file:// URL string representing the given path.
5421
 */
5422
std::string href_from_file(std::string_view path);
5423
}  // namespace ada
5424
5425
#endif  // ADA_IMPLEMENTATION_H
5426
/* end file include/ada/implementation.h */
5427
5428
#include <ostream>
5429
#include <string>
5430
#include <string_view>
5431
#include <unordered_map>
5432
#include <variant>
5433
#include <vector>
5434
5435
#if ADA_TESTING
5436
#include <iostream>
5437
#endif  // ADA_TESTING
5438
5439
#if ADA_INCLUDE_URL_PATTERN
5440
namespace ada {
5441
5442
enum class url_pattern_part_type : uint8_t {
5443
  // The part represents a simple fixed text string.
5444
  FIXED_TEXT,
5445
  // The part represents a matching group with a custom regular expression.
5446
  REGEXP,
5447
  // The part represents a matching group that matches code points up to the
5448
  // next separator code point. This is typically used for a named group like
5449
  // ":foo" that does not have a custom regular expression.
5450
  SEGMENT_WILDCARD,
5451
  // The part represents a matching group that greedily matches all code points.
5452
  // This is typically used for the "*" wildcard matching group.
5453
  FULL_WILDCARD,
5454
};
5455
5456
// Pattern type for fast-path matching optimization.
5457
// This allows skipping expensive regex evaluation for common simple patterns.
5458
enum class url_pattern_component_type : uint8_t {
5459
  // Pattern is "^$" - only matches empty string
5460
  EMPTY,
5461
  // Pattern is "^<literal>$" - exact string match (no regex needed)
5462
  EXACT_MATCH,
5463
  // Pattern is "^(.*)$" - matches anything (full wildcard)
5464
  FULL_WILDCARD,
5465
  // Pattern requires actual regex evaluation
5466
  REGEXP,
5467
};
5468
5469
enum class url_pattern_part_modifier : uint8_t {
5470
  // The part does not have a modifier.
5471
  none,
5472
  // The part has an optional modifier indicated by the U+003F (?) code point.
5473
  optional,
5474
  // The part has a "zero or more" modifier indicated by the U+002A (*) code
5475
  // point.
5476
  zero_or_more,
5477
  // The part has a "one or more" modifier indicated by the U+002B (+) code
5478
  // point.
5479
  one_or_more,
5480
};
5481
5482
// @see https://urlpattern.spec.whatwg.org/#part
5483
class url_pattern_part {
5484
 public:
5485
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5486
                   url_pattern_part_modifier _modifier)
5487
12.3k
      : type(_type), value(std::move(_value)), modifier(_modifier) {}
5488
5489
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5490
                   url_pattern_part_modifier _modifier, std::string&& _name,
5491
                   std::string&& _prefix, std::string&& _suffix)
5492
20.5k
      : type(_type),
5493
20.5k
        value(std::move(_value)),
5494
20.5k
        modifier(_modifier),
5495
20.5k
        name(std::move(_name)),
5496
20.5k
        prefix(std::move(_prefix)),
5497
20.5k
        suffix(std::move(_suffix)) {}
5498
  // A part has an associated type, a string, which must be set upon creation.
5499
  url_pattern_part_type type;
5500
  // A part has an associated value, a string, which must be set upon creation.
5501
  std::string value;
5502
  // A part has an associated modifier a string, which must be set upon
5503
  // creation.
5504
  url_pattern_part_modifier modifier;
5505
  // A part has an associated name, a string, initially the empty string.
5506
  std::string name{};
5507
  // A part has an associated prefix, a string, initially the empty string.
5508
  std::string prefix{};
5509
  // A part has an associated suffix, a string, initially the empty string.
5510
  std::string suffix{};
5511
5512
  inline bool is_regexp() const noexcept;
5513
};
5514
5515
// @see https://urlpattern.spec.whatwg.org/#options-header
5516
struct url_pattern_compile_component_options {
5517
  url_pattern_compile_component_options() = default;
5518
  explicit url_pattern_compile_component_options(
5519
      std::optional<char> new_delimiter = std::nullopt,
5520
      std::optional<char> new_prefix = std::nullopt)
5521
6
      : delimiter(new_delimiter), prefix(new_prefix) {}
5522
5523
  inline std::string_view get_delimiter() const ada_warn_unused;
5524
  inline std::string_view get_prefix() const ada_warn_unused;
5525
5526
  // @see https://urlpattern.spec.whatwg.org/#options-ignore-case
5527
  bool ignore_case = false;
5528
5529
  static url_pattern_compile_component_options DEFAULT;
5530
  static url_pattern_compile_component_options HOSTNAME;
5531
  static url_pattern_compile_component_options PATHNAME;
5532
5533
 private:
5534
  // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point
5535
  std::optional<char> delimiter{};
5536
  // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point
5537
  std::optional<char> prefix{};
5538
};
5539
5540
// The default options is an options struct with delimiter code point set to
5541
// the empty string and prefix code point set to the empty string.
5542
inline url_pattern_compile_component_options
5543
    url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt);
5544
5545
// The hostname options is an options struct with delimiter code point set
5546
// "." and prefix code point set to the empty string.
5547
inline url_pattern_compile_component_options
5548
    url_pattern_compile_component_options::HOSTNAME('.', std::nullopt);
5549
5550
// The pathname options is an options struct with delimiter code point set
5551
// "/" and prefix code point set to "/".
5552
inline url_pattern_compile_component_options
5553
    url_pattern_compile_component_options::PATHNAME('/', '/');
5554
5555
// A struct providing the URLPattern matching results for a single
5556
// URL component. The URLPatternComponentResult is only ever used
5557
// as a member attribute of a URLPatternResult struct. The
5558
// URLPatternComponentResult API is defined as part of the URLPattern
5559
// specification.
5560
struct url_pattern_component_result {
5561
  std::string input;
5562
  std::unordered_map<std::string, std::optional<std::string>> groups;
5563
5564
  bool operator==(const url_pattern_component_result&) const;
5565
5566
#if ADA_TESTING
5567
  friend void PrintTo(const url_pattern_component_result& result,
5568
                      std::ostream* os) {
5569
    *os << "input: '" << result.input << "', group: ";
5570
    for (const auto& group : result.groups) {
5571
      *os << "(" << group.first << ", " << group.second.value_or("undefined")
5572
          << ") ";
5573
    }
5574
  }
5575
#endif  // ADA_TESTING
5576
};
5577
5578
template <url_pattern_regex::regex_concept regex_provider>
5579
class url_pattern_component {
5580
 public:
5581
32.9k
  url_pattern_component() = default;
5582
5583
  // This function explicitly takes a std::string because it is moved.
5584
  // To avoid unnecessary copy, move each value while calling the constructor.
5585
  url_pattern_component(std::string&& new_pattern,
5586
                        typename regex_provider::regex_type&& new_regexp,
5587
                        std::vector<std::string>&& new_group_name_list,
5588
                        bool new_has_regexp_groups,
5589
                        url_pattern_component_type new_type,
5590
                        std::string&& new_exact_match_value = {})
5591
37.0k
      : regexp(std::move(new_regexp)),
5592
37.0k
        pattern(std::move(new_pattern)),
5593
37.0k
        group_name_list(std::move(new_group_name_list)),
5594
37.0k
        exact_match_value(std::move(new_exact_match_value)),
5595
37.0k
        has_regexp_groups(new_has_regexp_groups),
5596
37.0k
        type(new_type) {}
5597
5598
  // @see https://urlpattern.spec.whatwg.org/#compile-a-component
5599
  template <url_pattern_encoding_callback F>
5600
  static tl::expected<url_pattern_component, errors> compile(
5601
      std::string_view input, F& encoding_callback,
5602
      url_pattern_compile_component_options& options);
5603
5604
  // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result
5605
  url_pattern_component_result create_component_match_result(
5606
      std::string&& input,
5607
      std::vector<std::optional<std::string>>&& exec_result);
5608
5609
  // Fast path test that returns true/false without constructing result groups.
5610
  // Uses cached pattern type to skip regex evaluation for simple patterns.
5611
  bool fast_test(std::string_view input) const noexcept;
5612
5613
  // Fast path match that returns capture groups without regex for simple
5614
  // patterns. Returns nullopt if pattern doesn't match, otherwise returns
5615
  // capture groups.
5616
  std::optional<std::vector<std::optional<std::string>>> fast_match(
5617
      std::string_view input) const;
5618
5619
#if ADA_TESTING
5620
  friend void PrintTo(const url_pattern_component& component,
5621
                      std::ostream* os) {
5622
    *os << "pattern: '" << component.pattern
5623
        << "', has_regexp_groups: " << component.has_regexp_groups
5624
        << "group_name_list: ";
5625
    for (const auto& name : component.group_name_list) {
5626
      *os << name << ", ";
5627
    }
5628
  }
5629
#endif  // ADA_TESTING
5630
5631
  typename regex_provider::regex_type regexp{};
5632
  std::string pattern{};
5633
  std::vector<std::string> group_name_list{};
5634
  // For EXACT_MATCH type: the literal string to compare against
5635
  std::string exact_match_value{};
5636
  bool has_regexp_groups = false;
5637
  // Cached pattern type for fast-path optimization
5638
  url_pattern_component_type type = url_pattern_component_type::REGEXP;
5639
};
5640
5641
// A URLPattern input can be either a string or a URLPatternInit object.
5642
// If it is a string, it must be a valid UTF-8 string.
5643
using url_pattern_input = std::variant<std::string_view, url_pattern_init>;
5644
5645
// A struct providing the URLPattern matching results for all
5646
// components of a URL. The URLPatternResult API is defined as
5647
// part of the URLPattern specification.
5648
struct url_pattern_result {
5649
  std::vector<url_pattern_input> inputs;
5650
  url_pattern_component_result protocol;
5651
  url_pattern_component_result username;
5652
  url_pattern_component_result password;
5653
  url_pattern_component_result hostname;
5654
  url_pattern_component_result port;
5655
  url_pattern_component_result pathname;
5656
  url_pattern_component_result search;
5657
  url_pattern_component_result hash;
5658
};
5659
5660
struct url_pattern_options {
5661
  bool ignore_case = false;
5662
5663
#if ADA_TESTING
5664
  friend void PrintTo(const url_pattern_options& options, std::ostream* os) {
5665
    *os << "ignore_case: '" << options.ignore_case;
5666
  }
5667
#endif  // ADA_TESTING
5668
};
5669
5670
/**
5671
 * @brief URL pattern matching class implementing the URLPattern API.
5672
 *
5673
 * URLPattern provides a way to match URLs against patterns with wildcards
5674
 * and named capture groups. It's useful for routing, URL-based dispatching,
5675
 * and URL validation.
5676
 *
5677
 * Pattern syntax supports:
5678
 * - Literal text matching
5679
 * - Named groups: `:name` (matches up to the next separator)
5680
 * - Wildcards: `*` (matches everything)
5681
 * - Custom regex: `(pattern)`
5682
 * - Optional segments: `:name?`
5683
 * - Repeated segments: `:name+`, `:name*`
5684
 *
5685
 * @tparam regex_provider The regex implementation to use for pattern matching.
5686
 *         Must satisfy the url_pattern_regex::regex_concept.
5687
 *
5688
 * @note All string inputs must be valid UTF-8.
5689
 *
5690
 * @see https://urlpattern.spec.whatwg.org/
5691
 */
5692
template <url_pattern_regex::regex_concept regex_provider>
5693
class url_pattern {
5694
 public:
5695
4.11k
  url_pattern() = default;
5696
5697
  /**
5698
   * If non-null, base_url must pointer at a valid UTF-8 string.
5699
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec
5700
   */
5701
  result<std::optional<url_pattern_result>> exec(
5702
      const url_pattern_input& input,
5703
      const std::string_view* base_url = nullptr);
5704
5705
  /**
5706
   * If non-null, base_url must pointer at a valid UTF-8 string.
5707
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
5708
   */
5709
  result<bool> test(const url_pattern_input& input,
5710
                    const std::string_view* base_url = nullptr);
5711
5712
  /**
5713
   * @see https://urlpattern.spec.whatwg.org/#url-pattern-match
5714
   * This function expects a valid UTF-8 string if input is a string.
5715
   */
5716
  result<std::optional<url_pattern_result>> match(
5717
      const url_pattern_input& input,
5718
      const std::string_view* base_url_string = nullptr);
5719
5720
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol
5721
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
5722
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username
5723
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
5724
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password
5725
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
5726
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname
5727
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
5728
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port
5729
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
5730
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname
5731
  [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound;
5732
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search
5733
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
5734
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash
5735
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
5736
5737
  // If ignoreCase is true, the JavaScript regular expression created for each
5738
  // pattern must use the `vi` flag. Otherwise, they must use the `v` flag.
5739
  [[nodiscard]] bool ignore_case() const;
5740
5741
  // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups
5742
  [[nodiscard]] bool has_regexp_groups() const;
5743
5744
  // Helper to test all components at once. Returns true if all match.
5745
  [[nodiscard]] bool test_components(
5746
      std::string_view protocol, std::string_view username,
5747
      std::string_view password, std::string_view hostname,
5748
      std::string_view port, std::string_view pathname, std::string_view search,
5749
      std::string_view hash) const;
5750
5751
#if ADA_TESTING
5752
  friend void PrintTo(const url_pattern& c, std::ostream* os) {
5753
    *os << "protocol_component: '" << c.get_protocol() << ", ";
5754
    *os << "username_component: '" << c.get_username() << ", ";
5755
    *os << "password_component: '" << c.get_password() << ", ";
5756
    *os << "hostname_component: '" << c.get_hostname() << ", ";
5757
    *os << "port_component: '" << c.get_port() << ", ";
5758
    *os << "pathname_component: '" << c.get_pathname() << ", ";
5759
    *os << "search_component: '" << c.get_search() << ", ";
5760
    *os << "hash_component: '" << c.get_hash();
5761
  }
5762
#endif  // ADA_TESTING
5763
5764
  template <url_pattern_regex::regex_concept P>
5765
  friend tl::expected<url_pattern<P>, errors> parser::parse_url_pattern_impl(
5766
      std::variant<std::string_view, url_pattern_init>&& input,
5767
      const std::string_view* base_url, const url_pattern_options* options);
5768
5769
  /**
5770
   * @private
5771
   * We can not make this private due to a LLVM bug.
5772
   * Ref: https://github.com/ada-url/ada/pull/859
5773
   */
5774
  url_pattern_component<regex_provider> protocol_component{};
5775
  /**
5776
   * @private
5777
   * We can not make this private due to a LLVM bug.
5778
   * Ref: https://github.com/ada-url/ada/pull/859
5779
   */
5780
  url_pattern_component<regex_provider> username_component{};
5781
  /**
5782
   * @private
5783
   * We can not make this private due to a LLVM bug.
5784
   * Ref: https://github.com/ada-url/ada/pull/859
5785
   */
5786
  url_pattern_component<regex_provider> password_component{};
5787
  /**
5788
   * @private
5789
   * We can not make this private due to a LLVM bug.
5790
   * Ref: https://github.com/ada-url/ada/pull/859
5791
   */
5792
  url_pattern_component<regex_provider> hostname_component{};
5793
  /**
5794
   * @private
5795
   * We can not make this private due to a LLVM bug.
5796
   * Ref: https://github.com/ada-url/ada/pull/859
5797
   */
5798
  url_pattern_component<regex_provider> port_component{};
5799
  /**
5800
   * @private
5801
   * We can not make this private due to a LLVM bug.
5802
   * Ref: https://github.com/ada-url/ada/pull/859
5803
   */
5804
  url_pattern_component<regex_provider> pathname_component{};
5805
  /**
5806
   * @private
5807
   * We can not make this private due to a LLVM bug.
5808
   * Ref: https://github.com/ada-url/ada/pull/859
5809
   */
5810
  url_pattern_component<regex_provider> search_component{};
5811
  /**
5812
   * @private
5813
   * We can not make this private due to a LLVM bug.
5814
   * Ref: https://github.com/ada-url/ada/pull/859
5815
   */
5816
  url_pattern_component<regex_provider> hash_component{};
5817
  /**
5818
   * @private
5819
   * We can not make this private due to a LLVM bug.
5820
   * Ref: https://github.com/ada-url/ada/pull/859
5821
   */
5822
  bool ignore_case_ = false;
5823
};
5824
}  // namespace ada
5825
#endif  // ADA_INCLUDE_URL_PATTERN
5826
#endif
5827
/* end file include/ada/url_pattern.h */
5828
/* begin file include/ada/url_pattern_helpers.h */
5829
/**
5830
 * @file url_pattern_helpers.h
5831
 * @brief Declaration for the URLPattern helpers.
5832
 */
5833
#ifndef ADA_URL_PATTERN_HELPERS_H
5834
#define ADA_URL_PATTERN_HELPERS_H
5835
5836
5837
#include <string>
5838
#include <tuple>
5839
#include <vector>
5840
5841
#if ADA_INCLUDE_URL_PATTERN
5842
namespace ada {
5843
enum class errors : uint8_t;
5844
}
5845
5846
namespace ada::url_pattern_helpers {
5847
5848
// @see https://urlpattern.spec.whatwg.org/#token
5849
enum class token_type : uint8_t {
5850
  INVALID_CHAR,    // 0
5851
  OPEN,            // 1
5852
  CLOSE,           // 2
5853
  REGEXP,          // 3
5854
  NAME,            // 4
5855
  CHAR,            // 5
5856
  ESCAPED_CHAR,    // 6
5857
  OTHER_MODIFIER,  // 7
5858
  ASTERISK,        // 8
5859
  END,             // 9
5860
};
5861
5862
#ifdef ADA_TESTING
5863
std::string to_string(token_type type);
5864
#endif  // ADA_TESTING
5865
5866
// @see https://urlpattern.spec.whatwg.org/#tokenize-policy
5867
enum class token_policy {
5868
  strict,
5869
  lenient,
5870
};
5871
5872
// @see https://urlpattern.spec.whatwg.org/#tokens
5873
class token {
5874
 public:
5875
  token(token_type _type, size_t _index, std::string_view _value)
5876
341k
      : type(_type), index(_index), value(_value) {}
5877
5878
  // A token has an associated type, a string, initially "invalid-char".
5879
  token_type type = token_type::INVALID_CHAR;
5880
5881
  // A token has an associated index, a number, initially 0. It is the position
5882
  // of the first code point in the pattern string represented by the token.
5883
  size_t index = 0;
5884
5885
  // A token has an associated value, a string, initially the empty string. It
5886
  // contains the code points from the pattern string represented by the token.
5887
  std::string_view value{};
5888
};
5889
5890
// @see https://urlpattern.spec.whatwg.org/#pattern-parser
5891
template <url_pattern_encoding_callback F>
5892
class url_pattern_parser {
5893
 public:
5894
  url_pattern_parser(F& encoding_callback_,
5895
                     std::string_view segment_wildcard_regexp_)
5896
38.6k
      : encoding_callback(encoding_callback_),
5897
38.6k
        segment_wildcard_regexp(segment_wildcard_regexp_) {}
5898
5899
194k
  bool can_continue() const { return index < tokens.size(); }
5900
5901
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token
5902
  token* try_consume_token(token_type type);
5903
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token
5904
  token* try_consume_modifier_token();
5905
  // @see
5906
  // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token
5907
  token* try_consume_regexp_or_wildcard_token(const token* name_token);
5908
  // @see https://urlpattern.spec.whatwg.org/#consume-text
5909
  std::string consume_text();
5910
  // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token
5911
  bool consume_required_token(token_type type);
5912
  // @see
5913
  // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value
5914
  std::optional<errors> maybe_add_part_from_the_pending_fixed_value()
5915
      ada_warn_unused;
5916
  // @see https://urlpattern.spec.whatwg.org/#add-a-part
5917
  std::optional<errors> add_part(std::string_view prefix, token* name_token,
5918
                                 token* regexp_or_wildcard_token,
5919
                                 std::string_view suyffix,
5920
                                 token* modifier_token) ada_warn_unused;
5921
5922
  std::vector<token> tokens{};
5923
  F& encoding_callback;
5924
  std::string segment_wildcard_regexp;
5925
  std::vector<url_pattern_part> parts{};
5926
  std::string pending_fixed_value{};
5927
  size_t index = 0;
5928
  size_t next_numeric_name = 0;
5929
};
5930
5931
// @see https://urlpattern.spec.whatwg.org/#tokenizer
5932
class Tokenizer {
5933
 public:
5934
  explicit Tokenizer(std::string_view new_input, token_policy new_policy)
5935
46.8k
      : input(new_input), policy(new_policy) {}
5936
5937
  // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point
5938
  constexpr void get_next_code_point();
5939
5940
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
5941
  constexpr void seek_and_get_next_code_point(size_t index);
5942
5943
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
5944
5945
  void add_token(token_type type, size_t next_position, size_t value_position,
5946
                 size_t value_length);
5947
5948
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
5949
  void add_token_with_default_length(token_type type, size_t next_position,
5950
                                     size_t value_position);
5951
5952
  // @see
5953
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
5954
  void add_token_with_defaults(token_type type);
5955
5956
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
5957
  std::optional<errors> process_tokenizing_error(
5958
      size_t next_position, size_t value_position) ada_warn_unused;
5959
5960
  friend tl::expected<std::vector<token>, errors> tokenize(
5961
      std::string_view input, token_policy policy);
5962
5963
 private:
5964
  // has an associated input, a pattern string, initially the empty string.
5965
  std::string_view input;
5966
  // has an associated policy, a tokenize policy, initially "strict".
5967
  token_policy policy;
5968
  // has an associated token list, a token list, initially an empty list.
5969
  std::vector<token> token_list{};
5970
  // has an associated index, a number, initially 0.
5971
  size_t index = 0;
5972
  // has an associated next index, a number, initially 0.
5973
  size_t next_index = 0;
5974
  // has an associated code point, a Unicode code point, initially null.
5975
  char32_t code_point{};
5976
};
5977
5978
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
5979
template <url_pattern_regex::regex_concept regex_provider>
5980
struct constructor_string_parser {
5981
  explicit constructor_string_parser(std::string_view new_input,
5982
                                     std::vector<token>&& new_token_list)
5983
8.23k
      : input(new_input), token_list(std::move(new_token_list)) {}
5984
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
5985
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
5986
5987
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
5988
  enum class State {
5989
    INIT,
5990
    PROTOCOL,
5991
    AUTHORITY,
5992
    USERNAME,
5993
    PASSWORD,
5994
    HOSTNAME,
5995
    PORT,
5996
    PATHNAME,
5997
    SEARCH,
5998
    HASH,
5999
    DONE,
6000
  };
6001
6002
  // @see
6003
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6004
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6005
6006
 private:
6007
  // @see https://urlpattern.spec.whatwg.org/#rewind
6008
  constexpr void rewind();
6009
6010
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6011
  constexpr bool is_hash_prefix();
6012
6013
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6014
  constexpr bool is_search_prefix();
6015
6016
  // @see https://urlpattern.spec.whatwg.org/#change-state
6017
  void change_state(State state, size_t skip);
6018
6019
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6020
  constexpr bool is_group_open() const;
6021
6022
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6023
  constexpr bool is_group_close() const;
6024
6025
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6026
  constexpr bool is_protocol_suffix() const;
6027
6028
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6029
  constexpr bool next_is_authority_slashes() const;
6030
6031
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6032
  constexpr bool is_an_identity_terminator() const;
6033
6034
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6035
  constexpr bool is_pathname_start() const;
6036
6037
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6038
  constexpr bool is_password_prefix() const;
6039
6040
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6041
  constexpr bool is_an_ipv6_open() const;
6042
6043
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6044
  constexpr bool is_an_ipv6_close() const;
6045
6046
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6047
  constexpr bool is_port_prefix() const;
6048
6049
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6050
  constexpr bool is_non_special_pattern_char(size_t index,
6051
                                             uint32_t value) const;
6052
6053
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6054
  constexpr const token* get_safe_token(size_t index) const;
6055
6056
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6057
  std::string make_component_string();
6058
  // has an associated input, a string, which must be set upon creation.
6059
  std::string_view input;
6060
  // has an associated token list, a token list, which must be set upon
6061
  // creation.
6062
  std::vector<token> token_list;
6063
  // has an associated result, a URLPatternInit, initially set to a new
6064
  // URLPatternInit.
6065
  url_pattern_init result{};
6066
  // has an associated component start, a number, initially set to 0.
6067
  size_t component_start = 0;
6068
  // has an associated token index, a number, initially set to 0.
6069
  size_t token_index = 0;
6070
  // has an associated token increment, a number, initially set to 1.
6071
  size_t token_increment = 1;
6072
  // has an associated group depth, a number, initially set to 0.
6073
  size_t group_depth = 0;
6074
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6075
  // 0.
6076
  size_t hostname_ipv6_bracket_depth = 0;
6077
  // has an associated protocol matches a special scheme flag, a boolean,
6078
  // initially set to false.
6079
  bool protocol_matches_a_special_scheme_flag = false;
6080
  // has an associated state, a string, initially set to "init".
6081
  State state = State::INIT;
6082
};
6083
6084
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6085
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6086
6087
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6088
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6089
6090
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6091
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6092
6093
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6094
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6095
6096
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6097
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6098
    std::string_view input);
6099
6100
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6101
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6102
6103
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6104
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6105
    std::string_view input, std::string_view protocol);
6106
6107
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6108
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6109
6110
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6111
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6112
    std::string_view input);
6113
6114
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6115
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6116
6117
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6118
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6119
6120
// @see https://urlpattern.spec.whatwg.org/#tokenize
6121
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6122
                                                  token_policy policy);
6123
6124
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6125
std::string process_base_url_string(std::string_view input,
6126
                                    url_pattern_init::process_type type);
6127
6128
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6129
std::string escape_pattern_string(std::string_view input);
6130
6131
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6132
std::string escape_regexp_string(std::string_view input);
6133
6134
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6135
constexpr bool is_absolute_pathname(
6136
    std::string_view input, url_pattern_init::process_type type) noexcept;
6137
6138
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6139
template <url_pattern_encoding_callback F>
6140
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6141
    std::string_view input, url_pattern_compile_component_options& options,
6142
    F& encoding_callback);
6143
6144
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6145
std::string generate_pattern_string(
6146
    std::vector<url_pattern_part>& part_list,
6147
    url_pattern_compile_component_options& options);
6148
6149
// @see
6150
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6151
std::tuple<std::string, std::vector<std::string>>
6152
generate_regular_expression_and_name_list(
6153
    const std::vector<url_pattern_part>& part_list,
6154
    url_pattern_compile_component_options options);
6155
6156
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6157
bool is_ipv6_address(std::string_view input) noexcept;
6158
6159
// @see
6160
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6161
template <url_pattern_regex::regex_concept regex_provider>
6162
bool protocol_component_matches_special_scheme(
6163
    ada::url_pattern_component<regex_provider>& input);
6164
6165
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6166
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6167
6168
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6169
std::string generate_segment_wildcard_regexp(
6170
    url_pattern_compile_component_options options);
6171
6172
}  // namespace ada::url_pattern_helpers
6173
#endif  // ADA_INCLUDE_URL_PATTERN
6174
#endif
6175
/* end file include/ada/url_pattern_helpers.h */
6176
6177
#include <string>
6178
#include <string_view>
6179
#include <variant>
6180
6181
namespace ada::parser {
6182
#if ADA_INCLUDE_URL_PATTERN
6183
template <url_pattern_regex::regex_concept regex_provider>
6184
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6185
    std::variant<std::string_view, url_pattern_init>&& input,
6186
10.9k
    const std::string_view* base_url, const url_pattern_options* options) {
6187
  // Let init be null.
6188
10.9k
  url_pattern_init init;
6189
6190
  // If input is a scalar value string then:
6191
10.9k
  if (std::holds_alternative<std::string_view>(input)) {
6192
    // Set init to the result of running parse a constructor string given input.
6193
8.23k
    auto parse_result =
6194
8.23k
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6195
8.23k
            std::get<std::string_view>(input));
6196
8.23k
    if (!parse_result) {
6197
1.59k
      ada_log("constructor_string_parser::parse failed");
6198
1.59k
      return tl::unexpected(parse_result.error());
6199
1.59k
    }
6200
6.64k
    init = std::move(*parse_result);
6201
    // If baseURL is null and init["protocol"] does not exist, then throw a
6202
    // TypeError.
6203
6.64k
    if (!base_url && !init.protocol) {
6204
842
      ada_log("base url is null and protocol is not set");
6205
842
      return tl::unexpected(errors::type_error);
6206
842
    }
6207
6208
    // If baseURL is not null, set init["baseURL"] to baseURL.
6209
5.80k
    if (base_url) {
6210
4.42k
      init.base_url = std::string(*base_url);
6211
4.42k
    }
6212
5.80k
  } else {
6213
    // Assert: input is a URLPatternInit.
6214
2.74k
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6215
    // If baseURL is not null, then throw a TypeError.
6216
2.74k
    if (base_url) {
6217
2.74k
      ada_log("base url is not null");
6218
2.74k
      return tl::unexpected(errors::type_error);
6219
2.74k
    }
6220
    // Optimization: Avoid copy by moving the input value.
6221
    // Set init to input.
6222
0
    init = std::move(std::get<url_pattern_init>(input));
6223
0
  }
6224
6225
  // Let processedInit be the result of process a URLPatternInit given init,
6226
  // "pattern", null, null, null, null, null, null, null, and null.
6227
5.80k
  auto processed_init =
6228
5.80k
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6229
5.80k
  if (!processed_init) {
6230
1.68k
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6231
1.68k
    return tl::unexpected(processed_init.error());
6232
1.68k
  }
6233
6234
  // For each componentName of  "protocol", "username", "password", "hostname",
6235
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6236
  // not exist, then set processedInit[componentName] to "*".
6237
4.11k
  ADA_ASSERT_TRUE(processed_init.has_value());
6238
4.11k
  if (!processed_init->protocol) processed_init->protocol = "*";
6239
4.11k
  if (!processed_init->username) processed_init->username = "*";
6240
4.11k
  if (!processed_init->password) processed_init->password = "*";
6241
4.11k
  if (!processed_init->hostname) processed_init->hostname = "*";
6242
4.11k
  if (!processed_init->port) processed_init->port = "*";
6243
4.11k
  if (!processed_init->pathname) processed_init->pathname = "*";
6244
4.11k
  if (!processed_init->search) processed_init->search = "*";
6245
4.11k
  if (!processed_init->hash) processed_init->hash = "*";
6246
6247
4.11k
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6248
4.11k
  ada_log("-- processed_init->username: ", processed_init->username.value());
6249
4.11k
  ada_log("-- processed_init->password: ", processed_init->password.value());
6250
4.11k
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6251
4.11k
  ada_log("-- processed_init->port: ", processed_init->port.value());
6252
4.11k
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6253
4.11k
  ada_log("-- processed_init->search: ", processed_init->search.value());
6254
4.11k
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6255
6256
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6257
  // is a string which represents its corresponding default port in radix-10
6258
  // using ASCII digits then set processedInit["port"] to the empty string.
6259
  // TODO: Optimization opportunity.
6260
4.11k
  if (scheme::is_special(*processed_init->protocol)) {
6261
4.11k
    std::string_view port = processed_init->port.value();
6262
4.11k
    if (std::to_string(scheme::get_special_port(*processed_init->protocol)) ==
6263
4.11k
        port) {
6264
0
      processed_init->port->clear();
6265
0
    }
6266
4.11k
  }
6267
6268
  // Let urlPattern be a new URL pattern.
6269
4.11k
  url_pattern<regex_provider> url_pattern_{};
6270
6271
  // Set urlPattern's protocol component to the result of compiling a component
6272
  // given processedInit["protocol"], canonicalize a protocol, and default
6273
  // options.
6274
4.11k
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6275
4.11k
      processed_init->protocol.value(),
6276
4.11k
      url_pattern_helpers::canonicalize_protocol,
6277
4.11k
      url_pattern_compile_component_options::DEFAULT);
6278
4.11k
  if (!protocol_component) {
6279
0
    ada_log("url_pattern_component::compile failed for protocol ",
6280
0
            processed_init->protocol.value());
6281
0
    return tl::unexpected(protocol_component.error());
6282
0
  }
6283
4.11k
  url_pattern_.protocol_component = std::move(*protocol_component);
6284
6285
  // Set urlPattern's username component to the result of compiling a component
6286
  // given processedInit["username"], canonicalize a username, and default
6287
  // options.
6288
4.11k
  auto username_component = url_pattern_component<regex_provider>::compile(
6289
4.11k
      processed_init->username.value(),
6290
4.11k
      url_pattern_helpers::canonicalize_username,
6291
4.11k
      url_pattern_compile_component_options::DEFAULT);
6292
4.11k
  if (!username_component) {
6293
0
    ada_log("url_pattern_component::compile failed for username ",
6294
0
            processed_init->username.value());
6295
0
    return tl::unexpected(username_component.error());
6296
0
  }
6297
4.11k
  url_pattern_.username_component = std::move(*username_component);
6298
6299
  // Set urlPattern's password component to the result of compiling a component
6300
  // given processedInit["password"], canonicalize a password, and default
6301
  // options.
6302
4.11k
  auto password_component = url_pattern_component<regex_provider>::compile(
6303
4.11k
      processed_init->password.value(),
6304
4.11k
      url_pattern_helpers::canonicalize_password,
6305
4.11k
      url_pattern_compile_component_options::DEFAULT);
6306
4.11k
  if (!password_component) {
6307
0
    ada_log("url_pattern_component::compile failed for password ",
6308
0
            processed_init->password.value());
6309
0
    return tl::unexpected(password_component.error());
6310
0
  }
6311
4.11k
  url_pattern_.password_component = std::move(*password_component);
6312
6313
  // TODO: Optimization opportunity. The following if statement can be
6314
  // simplified.
6315
  // If the result running hostname pattern is an IPv6 address given
6316
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6317
  // to the result of compiling a component given processedInit["hostname"],
6318
  // canonicalize an IPv6 hostname, and hostname options.
6319
4.11k
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6320
0
    ada_log("processed_init->hostname is ipv6 address");
6321
    // then set urlPattern's hostname component to the result of compiling a
6322
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6323
    // and hostname options.
6324
0
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6325
0
        processed_init->hostname.value(),
6326
0
        url_pattern_helpers::canonicalize_ipv6_hostname,
6327
0
        url_pattern_compile_component_options::DEFAULT);
6328
0
    if (!hostname_component) {
6329
0
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6330
0
              processed_init->hostname.value());
6331
0
      return tl::unexpected(hostname_component.error());
6332
0
    }
6333
0
    url_pattern_.hostname_component = std::move(*hostname_component);
6334
4.11k
  } else {
6335
    // Otherwise, set urlPattern's hostname component to the result of compiling
6336
    // a component given processedInit["hostname"], canonicalize a hostname, and
6337
    // hostname options.
6338
4.11k
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6339
4.11k
        processed_init->hostname.value(),
6340
4.11k
        url_pattern_helpers::canonicalize_hostname,
6341
4.11k
        url_pattern_compile_component_options::HOSTNAME);
6342
4.11k
    if (!hostname_component) {
6343
0
      ada_log("url_pattern_component::compile failed for hostname ",
6344
0
              processed_init->hostname.value());
6345
0
      return tl::unexpected(hostname_component.error());
6346
0
    }
6347
4.11k
    url_pattern_.hostname_component = std::move(*hostname_component);
6348
4.11k
  }
6349
6350
  // Set urlPattern's port component to the result of compiling a component
6351
  // given processedInit["port"], canonicalize a port, and default options.
6352
4.11k
  auto port_component = url_pattern_component<regex_provider>::compile(
6353
4.11k
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6354
4.11k
      url_pattern_compile_component_options::DEFAULT);
6355
4.11k
  if (!port_component) {
6356
0
    ada_log("url_pattern_component::compile failed for port ",
6357
0
            processed_init->port.value());
6358
0
    return tl::unexpected(port_component.error());
6359
0
  }
6360
4.11k
  url_pattern_.port_component = std::move(*port_component);
6361
6362
  // Let compileOptions be a copy of the default options with the ignore case
6363
  // property set to options["ignoreCase"].
6364
4.11k
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6365
4.11k
  if (options) {
6366
1.37k
    compile_options.ignore_case = options->ignore_case;
6367
1.37k
  }
6368
6369
  // TODO: Optimization opportunity: Simplify this if statement.
6370
  // If the result of running protocol component matches a special scheme given
6371
  // urlPattern's protocol component is true, then:
6372
4.11k
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6373
4.11k
          regex_provider>(url_pattern_.protocol_component)) {
6374
    // Let pathCompileOptions be copy of the pathname options with the ignore
6375
    // case property set to options["ignoreCase"].
6376
4.11k
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6377
4.11k
    if (options) {
6378
1.37k
      path_compile_options.ignore_case = options->ignore_case;
6379
1.37k
    }
6380
6381
    // Set urlPattern's pathname component to the result of compiling a
6382
    // component given processedInit["pathname"], canonicalize a pathname, and
6383
    // pathCompileOptions.
6384
4.11k
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6385
4.11k
        processed_init->pathname.value(),
6386
4.11k
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6387
4.11k
    if (!pathname_component) {
6388
0
      ada_log("url_pattern_component::compile failed for pathname ",
6389
0
              processed_init->pathname.value());
6390
0
      return tl::unexpected(pathname_component.error());
6391
0
    }
6392
4.11k
    url_pattern_.pathname_component = std::move(*pathname_component);
6393
4.11k
  } else {
6394
    // Otherwise set urlPattern's pathname component to the result of compiling
6395
    // a component given processedInit["pathname"], canonicalize an opaque
6396
    // pathname, and compileOptions.
6397
0
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6398
0
        processed_init->pathname.value(),
6399
0
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6400
0
    if (!pathname_component) {
6401
0
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6402
0
              processed_init->pathname.value());
6403
0
      return tl::unexpected(pathname_component.error());
6404
0
    }
6405
0
    url_pattern_.pathname_component = std::move(*pathname_component);
6406
0
  }
6407
6408
  // Set urlPattern's search component to the result of compiling a component
6409
  // given processedInit["search"], canonicalize a search, and compileOptions.
6410
4.11k
  auto search_component = url_pattern_component<regex_provider>::compile(
6411
4.11k
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6412
4.11k
      compile_options);
6413
4.11k
  if (!search_component) {
6414
0
    ada_log("url_pattern_component::compile failed for search ",
6415
0
            processed_init->search.value());
6416
0
    return tl::unexpected(search_component.error());
6417
0
  }
6418
4.11k
  url_pattern_.search_component = std::move(*search_component);
6419
6420
  // Set urlPattern's hash component to the result of compiling a component
6421
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6422
4.11k
  auto hash_component = url_pattern_component<regex_provider>::compile(
6423
4.11k
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6424
4.11k
      compile_options);
6425
4.11k
  if (!hash_component) {
6426
0
    ada_log("url_pattern_component::compile failed for hash ",
6427
0
            processed_init->hash.value());
6428
0
    return tl::unexpected(hash_component.error());
6429
0
  }
6430
4.11k
  url_pattern_.hash_component = std::move(*hash_component);
6431
6432
  // Return urlPattern.
6433
4.11k
  return url_pattern_;
6434
4.11k
}
6435
#endif  // ADA_INCLUDE_URL_PATTERN
6436
6437
}  // namespace ada::parser
6438
6439
#endif  // ADA_PARSER_INL_H
6440
/* end file include/ada/parser-inl.h */
6441
/* begin file include/ada/scheme-inl.h */
6442
/**
6443
 * @file scheme-inl.h
6444
 * @brief Definitions for the URL scheme.
6445
 */
6446
#ifndef ADA_SCHEME_INL_H
6447
#define ADA_SCHEME_INL_H
6448
6449
6450
namespace ada::scheme {
6451
6452
/**
6453
 * @namespace ada::scheme::details
6454
 * @brief Includes the definitions for scheme specific entities
6455
 */
6456
namespace details {
6457
// for use with is_special and get_special_port
6458
// Spaces, if present, are removed from URL.
6459
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6460
                                                "ftp",  "wss", "file",  " "};
6461
// for use with get_special_port
6462
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6463
}  // namespace details
6464
6465
/****
6466
 * @private
6467
 * In is_special, get_scheme_type, and get_special_port, we
6468
 * use a standard hashing technique to find the index of the scheme in
6469
 * the is_special_list. The hashing technique is based on the size of
6470
 * the scheme and the first character of the scheme. It ensures that we
6471
 * do at most one string comparison per call. If the protocol is
6472
 * predictible (e.g., it is always "http"), we can get a better average
6473
 * performance by using a simpler approach where we loop and compare
6474
 * scheme with all possible protocols starting with the most likely
6475
 * protocol. Doing multiple comparisons may have a poor worst case
6476
 * performance, however. In this instance, we choose a potentially
6477
 * slightly lower best-case performance for a better worst-case
6478
 * performance. We can revisit this choice at any time.
6479
 *
6480
 * Reference:
6481
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6482
 * More C++ gems 17 (2000).
6483
 *
6484
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6485
 *
6486
 * Reference: https://github.com/ada-url/ada/issues/617
6487
 ****/
6488
6489
13.6k
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6490
13.6k
  if (scheme.empty()) {
6491
0
    return false;
6492
0
  }
6493
13.6k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6494
13.6k
  const std::string_view target = details::is_special_list[hash_value];
6495
13.6k
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6496
13.6k
}
6497
4.11k
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6498
4.11k
  if (scheme.empty()) {
6499
0
    return 0;
6500
0
  }
6501
4.11k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6502
4.11k
  const std::string_view target = details::is_special_list[hash_value];
6503
4.11k
  if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) {
6504
4.11k
    return details::special_ports[hash_value];
6505
4.11k
  } else {
6506
0
    return 0;
6507
0
  }
6508
4.11k
}
6509
0
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6510
0
  return details::special_ports[int(type)];
6511
0
}
6512
2.74k
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6513
2.74k
  if (scheme.empty()) {
6514
0
    return ada::scheme::NOT_SPECIAL;
6515
0
  }
6516
2.74k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6517
2.74k
  const std::string_view target = details::is_special_list[hash_value];
6518
2.74k
  if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) {
6519
2.74k
    return ada::scheme::type(hash_value);
6520
2.74k
  } else {
6521
0
    return ada::scheme::NOT_SPECIAL;
6522
0
  }
6523
2.74k
}
6524
6525
}  // namespace ada::scheme
6526
6527
#endif  // ADA_SCHEME_INL_H
6528
/* end file include/ada/scheme-inl.h */
6529
/* begin file include/ada/serializers.h */
6530
/**
6531
 * @file serializers.h
6532
 * @brief IP address serialization utilities.
6533
 *
6534
 * This header provides functions for converting IP addresses to their
6535
 * string representations according to the WHATWG URL Standard.
6536
 */
6537
#ifndef ADA_SERIALIZERS_H
6538
#define ADA_SERIALIZERS_H
6539
6540
6541
#include <array>
6542
#include <string>
6543
6544
/**
6545
 * @namespace ada::serializers
6546
 * @brief IP address serialization functions.
6547
 *
6548
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6549
 */
6550
namespace ada::serializers {
6551
6552
/**
6553
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6554
 * Used for :: compression in IPv6 serialization.
6555
 *
6556
 * @param address The 8 16-bit pieces of the IPv6 address.
6557
 * @param[out] compress Index of the start of the longest zero sequence.
6558
 * @param[out] compress_length Length of the longest zero sequence.
6559
 */
6560
void find_longest_sequence_of_ipv6_pieces(
6561
    const std::array<uint16_t, 8>& address, size_t& compress,
6562
    size_t& compress_length) noexcept;
6563
6564
/**
6565
 * Serializes an IPv6 address to its string representation.
6566
 *
6567
 * @param address The 8 16-bit pieces of the IPv6 address.
6568
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6569
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6570
 */
6571
std::string ipv6(const std::array<uint16_t, 8>& address) noexcept;
6572
6573
/**
6574
 * Serializes an IPv4 address to its dotted-decimal string representation.
6575
 *
6576
 * @param address The 32-bit IPv4 address as an integer.
6577
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6578
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6579
 */
6580
std::string ipv4(uint64_t address) noexcept;
6581
6582
}  // namespace ada::serializers
6583
6584
#endif  // ADA_SERIALIZERS_H
6585
/* end file include/ada/serializers.h */
6586
/* begin file include/ada/state.h */
6587
/**
6588
 * @file state.h
6589
 * @brief URL parser state machine states.
6590
 *
6591
 * Defines the states used by the URL parsing state machine as specified
6592
 * in the WHATWG URL Standard.
6593
 *
6594
 * @see https://url.spec.whatwg.org/#url-parsing
6595
 */
6596
#ifndef ADA_STATE_H
6597
#define ADA_STATE_H
6598
6599
6600
#include <string>
6601
6602
namespace ada {
6603
6604
/**
6605
 * @brief States in the URL parsing state machine.
6606
 *
6607
 * The URL parser processes input through a sequence of states, each handling
6608
 * a specific part of the URL syntax.
6609
 *
6610
 * @see https://url.spec.whatwg.org/#url-parsing
6611
 */
6612
enum class state {
6613
  /**
6614
   * @see https://url.spec.whatwg.org/#authority-state
6615
   */
6616
  AUTHORITY,
6617
6618
  /**
6619
   * @see https://url.spec.whatwg.org/#scheme-start-state
6620
   */
6621
  SCHEME_START,
6622
6623
  /**
6624
   * @see https://url.spec.whatwg.org/#scheme-state
6625
   */
6626
  SCHEME,
6627
6628
  /**
6629
   * @see https://url.spec.whatwg.org/#host-state
6630
   */
6631
  HOST,
6632
6633
  /**
6634
   * @see https://url.spec.whatwg.org/#no-scheme-state
6635
   */
6636
  NO_SCHEME,
6637
6638
  /**
6639
   * @see https://url.spec.whatwg.org/#fragment-state
6640
   */
6641
  FRAGMENT,
6642
6643
  /**
6644
   * @see https://url.spec.whatwg.org/#relative-state
6645
   */
6646
  RELATIVE_SCHEME,
6647
6648
  /**
6649
   * @see https://url.spec.whatwg.org/#relative-slash-state
6650
   */
6651
  RELATIVE_SLASH,
6652
6653
  /**
6654
   * @see https://url.spec.whatwg.org/#file-state
6655
   */
6656
  FILE,
6657
6658
  /**
6659
   * @see https://url.spec.whatwg.org/#file-host-state
6660
   */
6661
  FILE_HOST,
6662
6663
  /**
6664
   * @see https://url.spec.whatwg.org/#file-slash-state
6665
   */
6666
  FILE_SLASH,
6667
6668
  /**
6669
   * @see https://url.spec.whatwg.org/#path-or-authority-state
6670
   */
6671
  PATH_OR_AUTHORITY,
6672
6673
  /**
6674
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
6675
   */
6676
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
6677
6678
  /**
6679
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
6680
   */
6681
  SPECIAL_AUTHORITY_SLASHES,
6682
6683
  /**
6684
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
6685
   */
6686
  SPECIAL_RELATIVE_OR_AUTHORITY,
6687
6688
  /**
6689
   * @see https://url.spec.whatwg.org/#query-state
6690
   */
6691
  QUERY,
6692
6693
  /**
6694
   * @see https://url.spec.whatwg.org/#path-state
6695
   */
6696
  PATH,
6697
6698
  /**
6699
   * @see https://url.spec.whatwg.org/#path-start-state
6700
   */
6701
  PATH_START,
6702
6703
  /**
6704
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
6705
   */
6706
  OPAQUE_PATH,
6707
6708
  /**
6709
   * @see https://url.spec.whatwg.org/#port-state
6710
   */
6711
  PORT,
6712
};
6713
6714
/**
6715
 * Converts a parser state to its string name for debugging.
6716
 * @param s The state to convert.
6717
 * @return A string representation of the state.
6718
 */
6719
ada_warn_unused std::string to_string(ada::state s);
6720
6721
}  // namespace ada
6722
6723
#endif  // ADA_STATE_H
6724
/* end file include/ada/state.h */
6725
/* begin file include/ada/unicode.h */
6726
/**
6727
 * @file unicode.h
6728
 * @brief Definitions for all unicode specific functions.
6729
 */
6730
#ifndef ADA_UNICODE_H
6731
#define ADA_UNICODE_H
6732
6733
6734
#include <string>
6735
#include <string_view>
6736
#include <optional>
6737
6738
/**
6739
 * Unicode operations. These functions are not part of our public API and may
6740
 * change at any time.
6741
 *
6742
 * @private
6743
 * @namespace ada::unicode
6744
 * @brief Includes the definitions for unicode operations
6745
 */
6746
namespace ada::unicode {
6747
6748
/**
6749
 * @private
6750
 * We receive a UTF-8 string representing a domain name.
6751
 * If the string is percent encoded, we apply percent decoding.
6752
 *
6753
 * Given a domain, we need to identify its labels.
6754
 * They are separated by label-separators:
6755
 *
6756
 * U+002E (.) FULL STOP
6757
 * U+FF0E FULLWIDTH FULL STOP
6758
 * U+3002 IDEOGRAPHIC FULL STOP
6759
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
6760
 *
6761
 * They are all mapped to U+002E.
6762
 *
6763
 * We process each label into a string that should not exceed 63 octets.
6764
 * If the string is already punycode (starts with "xn--"), then we must
6765
 * scan it to look for unallowed code points.
6766
 * Otherwise, if the string is not pure ASCII, we need to transcode it
6767
 * to punycode by following RFC 3454 which requires us to
6768
 * - Map characters  (see section 3),
6769
 * - Normalize (see section 4),
6770
 * - Reject forbidden characters,
6771
 * - Check for right-to-left characters and if so, check all requirements (see
6772
 * section 6),
6773
 * - Optionally reject based on unassigned code points (section 7).
6774
 *
6775
 * The Unicode standard provides a table of code points with a mapping, a list
6776
 * of forbidden code points and so forth. This table is subject to change and
6777
 * will vary based on the implementation. For Unicode 15, the table is at
6778
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
6779
 * If you use ICU, they parse this table and map it to code using a Python
6780
 * script.
6781
 *
6782
 * The resulting strings should not exceed 255 octets according to RFC 1035
6783
 * section 2.3.4. ICU checks for label size and domain size, but these errors
6784
 * are ignored.
6785
 *
6786
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
6787
 *
6788
 */
6789
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
6790
              size_t first_percent);
6791
6792
/**
6793
 * @private
6794
 * Checks if the input has tab or newline characters.
6795
 *
6796
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
6797
 * enough that compilers like GCC can 'autovectorize it'.
6798
 */
6799
ada_really_inline bool has_tabs_or_newline(
6800
    std::string_view user_input) noexcept;
6801
6802
/**
6803
 * @private
6804
 * Checks if the input is a forbidden host code point.
6805
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
6806
 */
6807
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
6808
6809
/**
6810
 * @private
6811
 * Checks if the input contains a forbidden domain code point.
6812
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6813
 */
6814
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
6815
    const char* input, size_t length) noexcept;
6816
6817
/**
6818
 * @private
6819
 * Checks if the input contains a forbidden domain code point in which case
6820
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
6821
 * then the second bit is set to 1.
6822
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6823
 */
6824
ada_really_inline constexpr uint8_t
6825
contains_forbidden_domain_code_point_or_upper(const char* input,
6826
                                              size_t length) noexcept;
6827
6828
/**
6829
 * @private
6830
 * Checks if the input is a forbidden domain code point.
6831
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6832
 */
6833
ada_really_inline constexpr bool is_forbidden_domain_code_point(
6834
    char c) noexcept;
6835
6836
/**
6837
 * @private
6838
 * Checks if the input is alphanumeric, '+', '-' or '.'
6839
 */
6840
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
6841
6842
/**
6843
 * @private
6844
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
6845
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
6846
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
6847
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
6848
 */
6849
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
6850
6851
/**
6852
 * @private
6853
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
6854
 * inclusive.
6855
 */
6856
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
6857
6858
/**
6859
 * @private
6860
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
6861
 * character.
6862
 */
6863
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
6864
6865
/**
6866
 * @private
6867
 * Checks if the input is a C0 control or space character.
6868
 *
6869
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
6870
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
6871
 * SEPARATOR ONE, inclusive.
6872
 */
6873
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
6874
6875
/**
6876
 * @private
6877
 * Checks if the input is a ASCII tab or newline character.
6878
 *
6879
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
6880
 */
6881
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
6882
6883
/**
6884
 * @private
6885
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
6886
 * match for ".%2e", "%2e.", or "%2e%2e".
6887
 */
6888
ada_really_inline constexpr bool is_double_dot_path_segment(
6889
    std::string_view input) noexcept;
6890
6891
/**
6892
 * @private
6893
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
6894
 * match for "%2e".
6895
 */
6896
ada_really_inline constexpr bool is_single_dot_path_segment(
6897
    std::string_view input) noexcept;
6898
6899
/**
6900
 * @private
6901
 * @details ipv4 character might contain 0-9 or a-f character ranges.
6902
 */
6903
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
6904
6905
/**
6906
 * @private
6907
 * @details Convert hex to binary. Caller is responsible to ensure that
6908
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
6909
 */
6910
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
6911
6912
/**
6913
 * @private
6914
 * first_percent should be  = input.find('%')
6915
 *
6916
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
6917
 * @author Node.js
6918
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
6919
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
6920
 */
6921
std::string percent_decode(std::string_view input, size_t first_percent);
6922
6923
/**
6924
 * @private
6925
 * Returns a percent-encoding string whether percent encoding was needed or not.
6926
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
6927
 */
6928
std::string percent_encode(std::string_view input,
6929
                           const uint8_t character_set[]);
6930
/**
6931
 * @private
6932
 * Returns a percent-encoded string version of input, while starting the percent
6933
 * encoding at the provided index.
6934
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
6935
 */
6936
std::string percent_encode(std::string_view input,
6937
                           const uint8_t character_set[], size_t index);
6938
/**
6939
 * @private
6940
 * Returns true if percent encoding was needed, in which case, we store
6941
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
6942
 * true, the content is appended to 'out'.
6943
 * If percent encoding is not needed, out is left unchanged.
6944
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
6945
 */
6946
template <bool append>
6947
bool percent_encode(std::string_view input, const uint8_t character_set[],
6948
                    std::string& out);
6949
/**
6950
 * @private
6951
 * Returns the index at which percent encoding should start, or (equivalently),
6952
 * the length of the prefix that does not require percent encoding.
6953
 */
6954
ada_really_inline size_t percent_encode_index(std::string_view input,
6955
                                              const uint8_t character_set[]);
6956
/**
6957
 * @private
6958
 * Lowers the string in-place, assuming that the content is ASCII.
6959
 * Return true if the content was ASCII.
6960
 */
6961
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
6962
}  // namespace ada::unicode
6963
6964
#endif  // ADA_UNICODE_H
6965
/* end file include/ada/unicode.h */
6966
/* begin file include/ada/url_base-inl.h */
6967
/**
6968
 * @file url_base-inl.h
6969
 * @brief Inline functions for url base
6970
 */
6971
#ifndef ADA_URL_BASE_INL_H
6972
#define ADA_URL_BASE_INL_H
6973
6974
6975
#include <string>
6976
#if ADA_REGULAR_VISUAL_STUDIO
6977
#include <intrin.h>
6978
#endif  // ADA_REGULAR_VISUAL_STUDIO
6979
6980
namespace ada {
6981
6982
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
6983
13.7k
    const noexcept {
6984
13.7k
  return type != ada::scheme::NOT_SPECIAL;
6985
13.7k
}
6986
6987
0
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
6988
0
  return ada::scheme::get_special_port(type);
6989
0
}
6990
6991
[[nodiscard]] ada_really_inline uint16_t
6992
0
url_base::scheme_default_port() const noexcept {
6993
0
  return scheme::get_special_port(type);
6994
0
}
6995
6996
}  // namespace ada
6997
6998
#endif  // ADA_URL_BASE_INL_H
6999
/* end file include/ada/url_base-inl.h */
7000
/* begin file include/ada/url-inl.h */
7001
/**
7002
 * @file url-inl.h
7003
 * @brief Definitions for the URL
7004
 */
7005
#ifndef ADA_URL_INL_H
7006
#define ADA_URL_INL_H
7007
7008
7009
#include <charconv>
7010
#include <optional>
7011
#include <string>
7012
#if ADA_REGULAR_VISUAL_STUDIO
7013
#include <intrin.h>
7014
#endif  // ADA_REGULAR_VISUAL_STUDIO
7015
7016
namespace ada {
7017
0
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7018
0
  return !username.empty() || !password.empty();
7019
0
}
7020
0
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7021
0
  return port.has_value();
7022
0
}
7023
0
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7024
0
  return !host.has_value() || host.value().empty() ||
7025
0
         type == ada::scheme::type::FILE;
7026
0
}
7027
0
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7028
0
  if (!host.has_value()) {
7029
0
    return false;
7030
0
  }
7031
0
  return host.value().empty();
7032
0
}
7033
0
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7034
0
  return host.has_value();
7035
0
}
7036
0
inline std::ostream &operator<<(std::ostream &out, const ada::url &u) {
7037
0
  return out << u.to_string();
7038
0
}
7039
7040
0
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7041
0
  return path.size();
7042
0
}
7043
7044
0
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7045
0
  return path;
7046
0
}
7047
7048
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7049
0
    const noexcept {
7050
0
  url_components out{};
7051
0
7052
0
  // protocol ends with ':'. for example: "https:"
7053
0
  out.protocol_end = uint32_t(get_protocol().size());
7054
0
7055
0
  // Trailing index is always the next character of the current one.
7056
0
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7057
0
  size_t running_index = out.protocol_end;
7058
0
7059
0
  if (host.has_value()) {
7060
0
    // 2 characters for "//" and 1 character for starting index
7061
0
    out.host_start = out.protocol_end + 2;
7062
0
7063
0
    if (has_credentials()) {
7064
0
      out.username_end = uint32_t(out.host_start + username.size());
7065
0
7066
0
      out.host_start += uint32_t(username.size());
7067
0
7068
0
      if (!password.empty()) {
7069
0
        out.host_start += uint32_t(password.size() + 1);
7070
0
      }
7071
0
7072
0
      out.host_end = uint32_t(out.host_start + host.value().size());
7073
0
    } else {
7074
0
      out.username_end = out.host_start;
7075
0
7076
0
      // Host does not start with "@" if it does not include credentials.
7077
0
      out.host_end = uint32_t(out.host_start + host.value().size()) - 1;
7078
0
    }
7079
0
7080
0
    running_index = out.host_end + 1;
7081
0
  } else {
7082
0
    // Update host start and end date to the same index, since it does not
7083
0
    // exist.
7084
0
    out.host_start = out.protocol_end;
7085
0
    out.host_end = out.host_start;
7086
0
7087
0
    if (!has_opaque_path && path.starts_with("//")) {
7088
0
      // If url's host is null, url does not have an opaque path, url's path's
7089
0
      // size is greater than 1, and url's path[0] is the empty string, then
7090
0
      // append U+002F (/) followed by U+002E (.) to output.
7091
0
      running_index = out.protocol_end + 2;
7092
0
    } else {
7093
0
      running_index = out.protocol_end;
7094
0
    }
7095
0
  }
7096
0
7097
0
  if (port.has_value()) {
7098
0
    out.port = *port;
7099
0
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7100
0
  }
7101
0
7102
0
  out.pathname_start = uint32_t(running_index);
7103
0
7104
0
  running_index += path.size();
7105
0
7106
0
  if (query.has_value()) {
7107
0
    out.search_start = uint32_t(running_index);
7108
0
    running_index += get_search().size();
7109
0
    if (get_search().empty()) {
7110
0
      running_index++;
7111
0
    }
7112
0
  }
7113
0
7114
0
  if (hash.has_value()) {
7115
0
    out.hash_start = uint32_t(running_index);
7116
0
  }
7117
0
7118
0
  return out;
7119
0
}
7120
7121
0
inline void url::update_base_hostname(std::string_view input) { host = input; }
7122
7123
0
inline void url::update_unencoded_base_hash(std::string_view input) {
7124
  // We do the percent encoding
7125
0
  hash = unicode::percent_encode(input,
7126
0
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7127
0
}
7128
7129
inline void url::update_base_search(std::string_view input,
7130
0
                                    const uint8_t query_percent_encode_set[]) {
7131
0
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7132
0
}
7133
7134
0
inline void url::update_base_search(std::optional<std::string> &&input) {
7135
0
  query = std::move(input);
7136
0
}
7137
7138
0
inline void url::update_base_pathname(const std::string_view input) {
7139
0
  path = input;
7140
0
}
7141
7142
0
inline void url::update_base_username(const std::string_view input) {
7143
0
  username = input;
7144
0
}
7145
7146
0
inline void url::update_base_password(const std::string_view input) {
7147
0
  password = input;
7148
0
}
7149
7150
0
inline void url::update_base_port(std::optional<uint16_t> input) {
7151
0
  port = input;
7152
0
}
7153
7154
0
constexpr void url::clear_pathname() { path.clear(); }
7155
7156
0
constexpr void url::clear_search() { query = std::nullopt; }
7157
7158
0
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7159
0
  return hash.has_value();
7160
0
}
7161
7162
0
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7163
0
  return query.has_value();
7164
0
}
7165
7166
0
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7167
7168
0
inline void url::set_scheme(std::string &&new_scheme) noexcept {
7169
0
  type = ada::scheme::get_scheme_type(new_scheme);
7170
  // We only move the 'scheme' if it is non-special.
7171
0
  if (!is_special()) {
7172
0
    non_special_scheme = std::move(new_scheme);
7173
0
  }
7174
0
}
7175
7176
0
constexpr void url::copy_scheme(ada::url &&u) noexcept {
7177
0
  non_special_scheme = u.non_special_scheme;
7178
0
  type = u.type;
7179
0
}
7180
7181
0
constexpr void url::copy_scheme(const ada::url &u) {
7182
0
  non_special_scheme = u.non_special_scheme;
7183
0
  type = u.type;
7184
0
}
7185
7186
0
[[nodiscard]] ada_really_inline std::string url::get_href() const noexcept {
7187
0
  std::string output = get_protocol();
7188
0
7189
0
  if (host.has_value()) {
7190
0
    output += "//";
7191
0
    if (has_credentials()) {
7192
0
      output += username;
7193
0
      if (!password.empty()) {
7194
0
        output += ":" + get_password();
7195
0
      }
7196
0
      output += "@";
7197
0
    }
7198
0
    output += host.value();
7199
0
    if (port.has_value()) {
7200
0
      output += ":" + get_port();
7201
0
    }
7202
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7203
0
    // If url's host is null, url does not have an opaque path, url's path's
7204
0
    // size is greater than 1, and url's path[0] is the empty string, then
7205
0
    // append U+002F (/) followed by U+002E (.) to output.
7206
0
    output += "/.";
7207
0
  }
7208
0
  output += path;
7209
0
  if (query.has_value()) {
7210
0
    output += "?" + query.value();
7211
0
  }
7212
0
  if (hash.has_value()) {
7213
0
    output += "#" + hash.value();
7214
0
  }
7215
0
  return output;
7216
0
}
7217
7218
ada_really_inline size_t url::parse_port(std::string_view view,
7219
0
                                         bool check_trailing_content) noexcept {
7220
0
  ada_log("parse_port('", view, "') ", view.size());
7221
0
  if (!view.empty() && view[0] == '-') {
7222
0
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7223
0
    is_valid = false;
7224
0
    return 0;
7225
0
  }
7226
0
  uint16_t parsed_port{};
7227
0
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7228
0
  if (r.ec == std::errc::result_out_of_range) {
7229
0
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7230
0
    is_valid = false;
7231
0
    return 0;
7232
0
  }
7233
0
  ada_log("parse_port: ", parsed_port);
7234
0
  const auto consumed = size_t(r.ptr - view.data());
7235
0
  ada_log("parse_port: consumed ", consumed);
7236
0
  if (check_trailing_content) {
7237
0
    is_valid &=
7238
0
        (consumed == view.size() || view[consumed] == '/' ||
7239
0
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7240
0
  }
7241
0
  ada_log("parse_port: is_valid = ", is_valid);
7242
0
  if (is_valid) {
7243
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7244
0
    auto default_port = scheme_default_port();
7245
0
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7246
0
                         (default_port != parsed_port);
7247
0
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7248
0
                                                  : std::nullopt;
7249
0
  }
7250
0
  return consumed;
7251
0
}
7252
7253
}  // namespace ada
7254
7255
#endif  // ADA_URL_H
7256
/* end file include/ada/url-inl.h */
7257
/* begin file include/ada/url_components-inl.h */
7258
/**
7259
 * @file url_components.h
7260
 * @brief Declaration for the URL Components
7261
 */
7262
#ifndef ADA_URL_COMPONENTS_INL_H
7263
#define ADA_URL_COMPONENTS_INL_H
7264
7265
7266
namespace ada {
7267
7268
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7269
0
    const noexcept {
7270
0
  /**
7271
0
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7272
0
   *       |     |    |          | ^^^^|       |   |
7273
0
   *       |     |    |          | |   |       |   `----- hash_start
7274
0
   *       |     |    |          | |   |       `--------- search_start
7275
0
   *       |     |    |          | |   `----------------- pathname_start
7276
0
   *       |     |    |          | `--------------------- port
7277
0
   *       |     |    |          `----------------------- host_end
7278
0
   *       |     |    `---------------------------------- host_start
7279
0
   *       |     `--------------------------------------- username_end
7280
0
   *       `--------------------------------------------- protocol_end
7281
0
   */
7282
0
  // These conditions can be made more strict.
7283
0
  if (protocol_end == url_components::omitted) {
7284
0
    return false;
7285
0
  }
7286
0
  uint32_t index = protocol_end;
7287
0
7288
0
  if (username_end == url_components::omitted) {
7289
0
    return false;
7290
0
  }
7291
0
  if (username_end < index) {
7292
0
    return false;
7293
0
  }
7294
0
  index = username_end;
7295
0
7296
0
  if (host_start == url_components::omitted) {
7297
0
    return false;
7298
0
  }
7299
0
  if (host_start < index) {
7300
0
    return false;
7301
0
  }
7302
0
  index = host_start;
7303
0
7304
0
  if (port != url_components::omitted) {
7305
0
    if (port > 0xffff) {
7306
0
      return false;
7307
0
    }
7308
0
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7309
0
    if (index + port_length < index) {
7310
0
      return false;
7311
0
    }
7312
0
    index += port_length;
7313
0
  }
7314
0
7315
0
  if (pathname_start == url_components::omitted) {
7316
0
    return false;
7317
0
  }
7318
0
  if (pathname_start < index) {
7319
0
    return false;
7320
0
  }
7321
0
  index = pathname_start;
7322
0
7323
0
  if (search_start != url_components::omitted) {
7324
0
    if (search_start < index) {
7325
0
      return false;
7326
0
    }
7327
0
    index = search_start;
7328
0
  }
7329
0
7330
0
  if (hash_start != url_components::omitted) {
7331
0
    if (hash_start < index) {
7332
0
      return false;
7333
0
    }
7334
0
  }
7335
0
7336
0
  return true;
7337
0
}
7338
7339
}  // namespace ada
7340
#endif
7341
/* end file include/ada/url_components-inl.h */
7342
/* begin file include/ada/url_aggregator.h */
7343
/**
7344
 * @file url_aggregator.h
7345
 * @brief Declaration for the `ada::url_aggregator` class.
7346
 *
7347
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7348
 * URL using a single buffer with component offsets. This is the default and
7349
 * most memory-efficient URL representation in Ada.
7350
 *
7351
 * @see url.h for an alternative representation using separate strings
7352
 */
7353
#ifndef ADA_URL_AGGREGATOR_H
7354
#define ADA_URL_AGGREGATOR_H
7355
7356
#include <ostream>
7357
#include <string>
7358
#include <string_view>
7359
#include <variant>
7360
7361
7362
namespace ada {
7363
7364
namespace parser {}
7365
7366
/**
7367
 * @brief Memory-efficient URL representation using a single buffer.
7368
 *
7369
 * The `url_aggregator` stores the entire normalized URL in a single string
7370
 * buffer and tracks component boundaries using offsets. This design minimizes
7371
 * memory allocations and is ideal for read-mostly access patterns.
7372
 *
7373
 * Getter methods return `std::string_view` pointing into the internal buffer.
7374
 * These views are lightweight (no allocation) but become invalid if the
7375
 * url_aggregator is modified or destroyed.
7376
 *
7377
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7378
 * when any setter is called. Do not use a getter's result as input to a
7379
 * setter on the same object without copying first.
7380
 *
7381
 * @note This is the default URL type returned by `ada::parse()`.
7382
 *
7383
 * @see url For an alternative using separate std::string instances
7384
 */
7385
struct url_aggregator : url_base {
7386
4.42k
  url_aggregator() = default;
7387
0
  url_aggregator(const url_aggregator &u) = default;
7388
5.48k
  url_aggregator(url_aggregator &&u) noexcept = default;
7389
0
  url_aggregator &operator=(url_aggregator &&u) noexcept = default;
7390
0
  url_aggregator &operator=(const url_aggregator &u) = default;
7391
9.91k
  ~url_aggregator() override = default;
7392
7393
  /**
7394
   * The setter functions follow the steps defined in the URL Standard.
7395
   *
7396
   * The url_aggregator has a single buffer that contains the entire normalized
7397
   * URL. The various components are represented as offsets into that buffer.
7398
   * When you call get_pathname(), for example, you get a std::string_view that
7399
   * points into that buffer. If the url_aggregator is modified, the buffer may
7400
   * be reallocated, and the std::string_view you obtained earlier may become
7401
   * invalid. In particular, this implies that you cannot modify the URL using
7402
   * a setter function with a std::string_view that points into the
7403
   * url_aggregator E.g., the following is incorrect:
7404
   * url->set_hostname(url->get_pathname()).
7405
   * You must first copy the pathname to a separate string.
7406
   * std::string pathname(url->get_pathname());
7407
   * url->set_hostname(pathname);
7408
   *
7409
   * The caller is responsible for ensuring that the url_aggregator is not
7410
   * modified while any std::string_view obtained from it is in use.
7411
   */
7412
  bool set_href(std::string_view input);
7413
  bool set_host(std::string_view input);
7414
  bool set_hostname(std::string_view input);
7415
  bool set_protocol(std::string_view input);
7416
  bool set_username(std::string_view input);
7417
  bool set_password(std::string_view input);
7418
  bool set_port(std::string_view input);
7419
  bool set_pathname(std::string_view input);
7420
  void set_search(std::string_view input);
7421
  void set_hash(std::string_view input);
7422
7423
  /**
7424
   * Validates whether the hostname is a valid domain according to RFC 1034.
7425
   * @return `true` if the domain is valid, `false` otherwise.
7426
   */
7427
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7428
7429
  /**
7430
   * Returns the URL's origin (scheme + host + port for special URLs).
7431
   * @return A newly allocated string containing the serialized origin.
7432
   * @see https://url.spec.whatwg.org/#concept-url-origin
7433
   */
7434
  [[nodiscard]] std::string get_origin() const noexcept override;
7435
7436
  /**
7437
   * Returns the full serialized URL (the href) as a string_view.
7438
   * Does not allocate memory. The returned view becomes invalid if this
7439
   * url_aggregator is modified or destroyed.
7440
   * @return A string_view into the internal buffer.
7441
   * @see https://url.spec.whatwg.org/#dom-url-href
7442
   */
7443
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7444
      ada_lifetime_bound;
7445
7446
  /**
7447
   * Returns the URL's username component.
7448
   * Does not allocate memory. The returned view becomes invalid if this
7449
   * url_aggregator is modified or destroyed.
7450
   * @return A string_view of the username.
7451
   * @see https://url.spec.whatwg.org/#dom-url-username
7452
   */
7453
  [[nodiscard]] std::string_view get_username() const noexcept
7454
      ada_lifetime_bound;
7455
7456
  /**
7457
   * Returns the URL's password component.
7458
   * Does not allocate memory. The returned view becomes invalid if this
7459
   * url_aggregator is modified or destroyed.
7460
   * @return A string_view of the password.
7461
   * @see https://url.spec.whatwg.org/#dom-url-password
7462
   */
7463
  [[nodiscard]] std::string_view get_password() const noexcept
7464
      ada_lifetime_bound;
7465
7466
  /**
7467
   * Returns the URL's port as a string (e.g., "8080").
7468
   * Does not allocate memory. Returns empty view if no port is set.
7469
   * The returned view becomes invalid if this url_aggregator is modified.
7470
   * @return A string_view of the port.
7471
   * @see https://url.spec.whatwg.org/#dom-url-port
7472
   */
7473
  [[nodiscard]] std::string_view get_port() const noexcept ada_lifetime_bound;
7474
7475
  /**
7476
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
7477
   * Does not allocate memory. Returns empty view if no fragment is set.
7478
   * The returned view becomes invalid if this url_aggregator is modified.
7479
   * @return A string_view of the hash.
7480
   * @see https://url.spec.whatwg.org/#dom-url-hash
7481
   */
7482
  [[nodiscard]] std::string_view get_hash() const noexcept ada_lifetime_bound;
7483
7484
  /**
7485
   * Returns the URL's host and port (e.g., "example.com:8080").
7486
   * Does not allocate memory. Returns empty view if no host is set.
7487
   * The returned view becomes invalid if this url_aggregator is modified.
7488
   * @return A string_view of host:port.
7489
   * @see https://url.spec.whatwg.org/#dom-url-host
7490
   */
7491
  [[nodiscard]] std::string_view get_host() const noexcept ada_lifetime_bound;
7492
7493
  /**
7494
   * Returns the URL's hostname (without port).
7495
   * Does not allocate memory. Returns empty view if no host is set.
7496
   * The returned view becomes invalid if this url_aggregator is modified.
7497
   * @return A string_view of the hostname.
7498
   * @see https://url.spec.whatwg.org/#dom-url-hostname
7499
   */
7500
  [[nodiscard]] std::string_view get_hostname() const noexcept
7501
      ada_lifetime_bound;
7502
7503
  /**
7504
   * Returns the URL's path component.
7505
   * Does not allocate memory. The returned view becomes invalid if this
7506
   * url_aggregator is modified or destroyed.
7507
   * @return A string_view of the pathname.
7508
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7509
   */
7510
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept
7511
      ada_lifetime_bound;
7512
7513
  /**
7514
   * Returns the byte length of the pathname without creating a string.
7515
   * @return Size of the pathname in bytes.
7516
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7517
   */
7518
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
7519
7520
  /**
7521
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
7522
   * Does not allocate memory. Returns empty view if no query is set.
7523
   * The returned view becomes invalid if this url_aggregator is modified.
7524
   * @return A string_view of the search/query.
7525
   * @see https://url.spec.whatwg.org/#dom-url-search
7526
   */
7527
  [[nodiscard]] std::string_view get_search() const noexcept ada_lifetime_bound;
7528
7529
  /**
7530
   * Returns the URL's scheme followed by a colon (e.g., "https:").
7531
   * Does not allocate memory. The returned view becomes invalid if this
7532
   * url_aggregator is modified or destroyed.
7533
   * @return A string_view of the protocol.
7534
   * @see https://url.spec.whatwg.org/#dom-url-protocol
7535
   */
7536
  [[nodiscard]] std::string_view get_protocol() const noexcept
7537
      ada_lifetime_bound;
7538
7539
  /**
7540
   * Checks if the URL has credentials (non-empty username or password).
7541
   * @return `true` if username or password is non-empty, `false` otherwise.
7542
   */
7543
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
7544
      const noexcept;
7545
7546
  /**
7547
   * Returns the URL component offsets for efficient serialization.
7548
   *
7549
   * The components represent byte offsets into the serialized URL:
7550
   * ```
7551
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7552
   *       |     |    |          | ^^^^|       |   |
7553
   *       |     |    |          | |   |       |   `----- hash_start
7554
   *       |     |    |          | |   |       `--------- search_start
7555
   *       |     |    |          | |   `----------------- pathname_start
7556
   *       |     |    |          | `--------------------- port
7557
   *       |     |    |          `----------------------- host_end
7558
   *       |     |    `---------------------------------- host_start
7559
   *       |     `--------------------------------------- username_end
7560
   *       `--------------------------------------------- protocol_end
7561
   * ```
7562
   * @return A constant reference to the url_components struct.
7563
   * @see https://github.com/servo/rust-url
7564
   */
7565
  [[nodiscard]] ada_really_inline const url_components &get_components()
7566
      const noexcept;
7567
7568
  /**
7569
   * Returns a JSON string representation of this URL for debugging.
7570
   * @return A JSON-formatted string with all URL components.
7571
   */
7572
  [[nodiscard]] std::string to_string() const override;
7573
7574
  /**
7575
   * Returns a visual diagram showing component boundaries in the URL.
7576
   * Useful for debugging and understanding URL structure.
7577
   * @return A multi-line string diagram.
7578
   */
7579
  [[nodiscard]] std::string to_diagram() const;
7580
7581
  /**
7582
   * Validates internal consistency of component offsets (for debugging).
7583
   * @return `true` if offsets are consistent, `false` if corrupted.
7584
   */
7585
  [[nodiscard]] constexpr bool validate() const noexcept;
7586
7587
  /**
7588
   * Checks if the URL has an empty hostname (host is set but empty string).
7589
   * @return `true` if host exists but is empty, `false` otherwise.
7590
   */
7591
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
7592
7593
  /**
7594
   * Checks if the URL has a hostname (including empty hostnames).
7595
   * @return `true` if host is present, `false` otherwise.
7596
   */
7597
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
7598
7599
  /**
7600
   * Checks if the URL has a non-empty username.
7601
   * @return `true` if username is non-empty, `false` otherwise.
7602
   */
7603
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
7604
7605
  /**
7606
   * Checks if the URL has a non-empty password.
7607
   * @return `true` if password is non-empty, `false` otherwise.
7608
   */
7609
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
7610
7611
  /**
7612
   * Checks if the URL has a non-default port explicitly specified.
7613
   * @return `true` if a port is present, `false` otherwise.
7614
   */
7615
  [[nodiscard]] constexpr bool has_port() const noexcept;
7616
7617
  /**
7618
   * Checks if the URL has a password component (may be empty).
7619
   * @return `true` if password is present, `false` otherwise.
7620
   */
7621
  [[nodiscard]] constexpr bool has_password() const noexcept;
7622
7623
  /**
7624
   * Checks if the URL has a fragment/hash component.
7625
   * @return `true` if hash is present, `false` otherwise.
7626
   */
7627
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
7628
7629
  /**
7630
   * Checks if the URL has a query/search component.
7631
   * @return `true` if query is present, `false` otherwise.
7632
   */
7633
  [[nodiscard]] constexpr bool has_search() const noexcept override;
7634
7635
  /**
7636
   * Removes the port from the URL.
7637
   */
7638
  inline void clear_port();
7639
7640
  /**
7641
   * Removes the hash/fragment from the URL.
7642
   */
7643
  inline void clear_hash();
7644
7645
  /**
7646
   * Removes the query/search string from the URL.
7647
   */
7648
  inline void clear_search() override;
7649
7650
 private:
7651
  // helper methods
7652
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
7653
      url_aggregator &url) noexcept;
7654
  // parse_url methods
7655
  friend url_aggregator parser::parse_url<url_aggregator>(
7656
      std::string_view, const url_aggregator *);
7657
7658
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
7659
      std::string_view, const url_aggregator *);
7660
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
7661
      std::string_view, const url_aggregator *);
7662
7663
#if ADA_INCLUDE_URL_PATTERN
7664
  // url_pattern methods
7665
  template <url_pattern_regex::regex_concept regex_provider>
7666
  friend tl::expected<url_pattern<regex_provider>, errors>
7667
  parse_url_pattern_impl(
7668
      std::variant<std::string_view, url_pattern_init> &&input,
7669
      const std::string_view *base_url, const url_pattern_options *options);
7670
#endif  // ADA_INCLUDE_URL_PATTERN
7671
7672
  std::string buffer{};
7673
  url_components components{};
7674
7675
  /**
7676
   * Returns true if neither the search, nor the hash nor the pathname
7677
   * have been set.
7678
   * @return true if the buffer is ready to receive the path.
7679
   */
7680
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
7681
7682
  inline void add_authority_slashes_if_needed() noexcept;
7683
7684
  /**
7685
   * To optimize performance, you may indicate how much memory to allocate
7686
   * within this instance.
7687
   */
7688
  constexpr void reserve(uint32_t capacity);
7689
7690
  ada_really_inline size_t parse_port(
7691
      std::string_view view, bool check_trailing_content) noexcept override;
7692
7693
0
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
7694
0
    return this->parse_port(view, false);
7695
0
  }
7696
7697
  /**
7698
   * Return true on success. The 'in_place' parameter indicates whether the
7699
   * the string_view input is pointing in the buffer. When in_place is false,
7700
   * we must nearly always update the buffer.
7701
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
7702
   */
7703
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
7704
7705
  /**
7706
   * Return true on success.
7707
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
7708
   */
7709
  [[nodiscard]] bool parse_ipv6(std::string_view input);
7710
7711
  /**
7712
   * Return true on success.
7713
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
7714
   */
7715
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
7716
7717
  ada_really_inline void parse_path(std::string_view input);
7718
7719
  /**
7720
   * A URL cannot have a username/password/port if its host is null or the empty
7721
   * string, or its scheme is "file".
7722
   */
7723
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
7724
7725
  template <bool override_hostname = false>
7726
  bool set_host_or_hostname(std::string_view input);
7727
7728
  ada_really_inline bool parse_host(std::string_view input);
7729
7730
  inline void update_base_authority(std::string_view base_buffer,
7731
                                    const url_components &base);
7732
  inline void update_unencoded_base_hash(std::string_view input);
7733
  inline void update_base_hostname(std::string_view input);
7734
  inline void update_base_search(std::string_view input);
7735
  inline void update_base_search(std::string_view input,
7736
                                 const uint8_t *query_percent_encode_set);
7737
  inline void update_base_pathname(std::string_view input);
7738
  inline void update_base_username(std::string_view input);
7739
  inline void append_base_username(std::string_view input);
7740
  inline void update_base_password(std::string_view input);
7741
  inline void append_base_password(std::string_view input);
7742
  inline void update_base_port(uint32_t input);
7743
  inline void append_base_pathname(std::string_view input);
7744
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
7745
  constexpr void clear_hostname();
7746
  constexpr void clear_password();
7747
  constexpr void clear_pathname() override;
7748
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
7749
  void delete_dash_dot();
7750
  inline void consume_prepared_path(std::string_view input);
7751
  template <bool has_state_override = false>
7752
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
7753
      std::string_view input);
7754
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
7755
                                                std::string_view input);
7756
  [[nodiscard]] constexpr bool has_authority() const noexcept;
7757
  constexpr void set_protocol_as_file();
7758
  inline void set_scheme(std::string_view new_scheme) noexcept;
7759
  /**
7760
   * Fast function to set the scheme from a view with a colon in the
7761
   * buffer, does not change type.
7762
   */
7763
  inline void set_scheme_from_view_with_colon(
7764
      std::string_view new_scheme_with_colon) noexcept;
7765
  inline void copy_scheme(const url_aggregator &u) noexcept;
7766
7767
  inline void update_host_to_base_host(const std::string_view input) noexcept;
7768
7769
};  // url_aggregator
7770
7771
inline std::ostream &operator<<(std::ostream &out, const url &u);
7772
}  // namespace ada
7773
7774
#endif
7775
/* end file include/ada/url_aggregator.h */
7776
/* begin file include/ada/url_aggregator-inl.h */
7777
/**
7778
 * @file url_aggregator-inl.h
7779
 * @brief Inline functions for url aggregator
7780
 */
7781
#ifndef ADA_URL_AGGREGATOR_INL_H
7782
#define ADA_URL_AGGREGATOR_INL_H
7783
7784
/* begin file include/ada/unicode-inl.h */
7785
/**
7786
 * @file unicode-inl.h
7787
 * @brief Definitions for unicode operations.
7788
 */
7789
#ifndef ADA_UNICODE_INL_H
7790
#define ADA_UNICODE_INL_H
7791
7792
/**
7793
 * Unicode operations. These functions are not part of our public API and may
7794
 * change at any time.
7795
 *
7796
 * private
7797
 * @namespace ada::unicode
7798
 * @brief Includes the declarations for unicode operations
7799
 */
7800
namespace ada::unicode {
7801
ada_really_inline size_t percent_encode_index(const std::string_view input,
7802
0
                                              const uint8_t character_set[]) {
7803
0
  const char* data = input.data();
7804
0
  const size_t size = input.size();
7805
7806
  // Process 8 bytes at a time using unrolled loop
7807
0
  size_t i = 0;
7808
0
  for (; i + 8 <= size; i += 8) {
7809
0
    unsigned char chunk[8];
7810
0
    std::memcpy(&chunk, data + i,
7811
0
                8);  // entices compiler to unconditionally process 8 characters
7812
7813
    // Check 8 characters at once
7814
0
    for (size_t j = 0; j < 8; j++) {
7815
0
      if (character_sets::bit_at(character_set, chunk[j])) {
7816
0
        return i + j;
7817
0
      }
7818
0
    }
7819
0
  }
7820
7821
  // Handle remaining bytes
7822
0
  for (; i < size; i++) {
7823
0
    if (character_sets::bit_at(character_set, data[i])) {
7824
0
      return i;
7825
0
    }
7826
0
  }
7827
7828
0
  return size;
7829
0
}
7830
}  // namespace ada::unicode
7831
7832
#endif  // ADA_UNICODE_INL_H
7833
/* end file include/ada/unicode-inl.h */
7834
7835
#include <charconv>
7836
#include <ostream>
7837
#include <string_view>
7838
7839
namespace ada {
7840
7841
inline void url_aggregator::update_base_authority(
7842
0
    std::string_view base_buffer, const ada::url_components &base) {
7843
0
  std::string_view input = base_buffer.substr(
7844
0
      base.protocol_end, base.host_start - base.protocol_end);
7845
0
  ada_log("url_aggregator::update_base_authority ", input);
7846
7847
0
  bool input_starts_with_dash = input.starts_with("//");
7848
0
  uint32_t diff = components.host_start - components.protocol_end;
7849
7850
0
  buffer.erase(components.protocol_end,
7851
0
               components.host_start - components.protocol_end);
7852
0
  components.username_end = components.protocol_end;
7853
7854
0
  if (input_starts_with_dash) {
7855
0
    input.remove_prefix(2);
7856
0
    diff += 2;  // add "//"
7857
0
    buffer.insert(components.protocol_end, "//");
7858
0
    components.username_end += 2;
7859
0
  }
7860
7861
0
  size_t password_delimiter = input.find(':');
7862
7863
  // Check if input contains both username and password by checking the
7864
  // delimiter: ":" A typical input that contains authority would be "user:pass"
7865
0
  if (password_delimiter != std::string_view::npos) {
7866
    // Insert both username and password
7867
0
    std::string_view username = input.substr(0, password_delimiter);
7868
0
    std::string_view password = input.substr(password_delimiter + 1);
7869
7870
0
    buffer.insert(components.protocol_end + diff, username);
7871
0
    diff += uint32_t(username.size());
7872
0
    buffer.insert(components.protocol_end + diff, ":");
7873
0
    components.username_end = components.protocol_end + diff;
7874
0
    buffer.insert(components.protocol_end + diff + 1, password);
7875
0
    diff += uint32_t(password.size()) + 1;
7876
0
  } else if (!input.empty()) {
7877
    // Insert only username
7878
0
    buffer.insert(components.protocol_end + diff, input);
7879
0
    components.username_end =
7880
0
        components.protocol_end + diff + uint32_t(input.size());
7881
0
    diff += uint32_t(input.size());
7882
0
  }
7883
7884
0
  components.host_start += diff;
7885
7886
0
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
7887
0
    buffer.insert(components.host_start, "@");
7888
0
    diff++;
7889
0
  }
7890
0
  components.host_end += diff;
7891
0
  components.pathname_start += diff;
7892
0
  if (components.search_start != url_components::omitted) {
7893
0
    components.search_start += diff;
7894
0
  }
7895
0
  if (components.hash_start != url_components::omitted) {
7896
0
    components.hash_start += diff;
7897
0
  }
7898
0
}
7899
7900
0
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
7901
0
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
7902
0
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
7903
0
          " bytes] components.hash_start = ", components.hash_start);
7904
0
  ADA_ASSERT_TRUE(validate());
7905
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
7906
0
  if (components.hash_start != url_components::omitted) {
7907
0
    buffer.resize(components.hash_start);
7908
0
  }
7909
0
  components.hash_start = uint32_t(buffer.size());
7910
0
  buffer += "#";
7911
0
  bool encoding_required = unicode::percent_encode<true>(
7912
0
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
7913
  // When encoding_required is false, then buffer is left unchanged, and percent
7914
  // encoding was not deemed required.
7915
0
  if (!encoding_required) {
7916
0
    buffer.append(input);
7917
0
  }
7918
0
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
7919
0
          buffer, "' [", buffer.size(), " bytes]");
7920
0
  ADA_ASSERT_TRUE(validate());
7921
0
}
7922
7923
ada_really_inline uint32_t url_aggregator::replace_and_resize(
7924
5.48k
    uint32_t start, uint32_t end, std::string_view input) {
7925
5.48k
  uint32_t current_length = end - start;
7926
5.48k
  uint32_t input_size = uint32_t(input.size());
7927
5.48k
  uint32_t new_difference = input_size - current_length;
7928
7929
5.48k
  if (current_length == 0) {
7930
5.48k
    buffer.insert(start, input);
7931
5.48k
  } else if (input_size == current_length) {
7932
0
    buffer.replace(start, input_size, input);
7933
0
  } else if (input_size < current_length) {
7934
0
    buffer.erase(start, current_length - input_size);
7935
0
    buffer.replace(start, input_size, input);
7936
0
  } else {
7937
0
    buffer.replace(start, current_length, input.substr(0, current_length));
7938
0
    buffer.insert(start + current_length, input.substr(current_length));
7939
0
  }
7940
7941
5.48k
  return new_difference;
7942
5.48k
}
7943
7944
2.74k
inline void url_aggregator::update_base_hostname(const std::string_view input) {
7945
2.74k
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
7946
2.74k
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
7947
2.74k
  ADA_ASSERT_TRUE(validate());
7948
2.74k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
7949
7950
  // This next line is required for when parsing a URL like `foo://`
7951
2.74k
  add_authority_slashes_if_needed();
7952
7953
2.74k
  bool has_credentials = components.protocol_end + 2 < components.host_start;
7954
2.74k
  uint32_t new_difference =
7955
2.74k
      replace_and_resize(components.host_start, components.host_end, input);
7956
7957
2.74k
  if (has_credentials) {
7958
0
    buffer.insert(components.host_start, "@");
7959
0
    new_difference++;
7960
0
  }
7961
2.74k
  components.host_end += new_difference;
7962
2.74k
  components.pathname_start += new_difference;
7963
2.74k
  if (components.search_start != url_components::omitted) {
7964
0
    components.search_start += new_difference;
7965
0
  }
7966
2.74k
  if (components.hash_start != url_components::omitted) {
7967
0
    components.hash_start += new_difference;
7968
0
  }
7969
2.74k
  ADA_ASSERT_TRUE(validate());
7970
2.74k
}
7971
7972
[[nodiscard]] ada_really_inline uint32_t
7973
2.74k
url_aggregator::get_pathname_length() const noexcept {
7974
2.74k
  ada_log("url_aggregator::get_pathname_length");
7975
2.74k
  uint32_t ending_index = uint32_t(buffer.size());
7976
2.74k
  if (components.search_start != url_components::omitted) {
7977
0
    ending_index = components.search_start;
7978
2.74k
  } else if (components.hash_start != url_components::omitted) {
7979
0
    ending_index = components.hash_start;
7980
0
  }
7981
2.74k
  return ending_index - components.pathname_start;
7982
2.74k
}
7983
7984
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
7985
0
    const noexcept {
7986
0
  return buffer.size() == components.pathname_start;
7987
0
}
7988
7989
0
inline void url_aggregator::update_base_search(std::string_view input) {
7990
0
  ada_log("url_aggregator::update_base_search ", input);
7991
0
  ADA_ASSERT_TRUE(validate());
7992
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
7993
0
  if (input.empty()) {
7994
0
    clear_search();
7995
0
    return;
7996
0
  }
7997
7998
0
  if (input[0] == '?') {
7999
0
    input.remove_prefix(1);
8000
0
  }
8001
8002
0
  if (components.hash_start == url_components::omitted) {
8003
0
    if (components.search_start == url_components::omitted) {
8004
0
      components.search_start = uint32_t(buffer.size());
8005
0
      buffer += "?";
8006
0
    } else {
8007
0
      buffer.resize(components.search_start + 1);
8008
0
    }
8009
8010
0
    buffer.append(input);
8011
0
  } else {
8012
0
    if (components.search_start == url_components::omitted) {
8013
0
      components.search_start = components.hash_start;
8014
0
    } else {
8015
0
      buffer.erase(components.search_start,
8016
0
                   components.hash_start - components.search_start);
8017
0
      components.hash_start = components.search_start;
8018
0
    }
8019
8020
0
    buffer.insert(components.search_start, "?");
8021
0
    buffer.insert(components.search_start + 1, input);
8022
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8023
0
  }
8024
8025
0
  ADA_ASSERT_TRUE(validate());
8026
0
}
8027
8028
inline void url_aggregator::update_base_search(
8029
0
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8030
0
  ada_log("url_aggregator::update_base_search ", input,
8031
0
          " with encoding parameter ", to_string(), "\n", to_diagram());
8032
0
  ADA_ASSERT_TRUE(validate());
8033
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8034
8035
0
  if (components.hash_start == url_components::omitted) {
8036
0
    if (components.search_start == url_components::omitted) {
8037
0
      components.search_start = uint32_t(buffer.size());
8038
0
      buffer += "?";
8039
0
    } else {
8040
0
      buffer.resize(components.search_start + 1);
8041
0
    }
8042
8043
0
    bool encoding_required =
8044
0
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8045
    // When encoding_required is false, then buffer is left unchanged, and
8046
    // percent encoding was not deemed required.
8047
0
    if (!encoding_required) {
8048
0
      buffer.append(input);
8049
0
    }
8050
0
  } else {
8051
0
    if (components.search_start == url_components::omitted) {
8052
0
      components.search_start = components.hash_start;
8053
0
    } else {
8054
0
      buffer.erase(components.search_start,
8055
0
                   components.hash_start - components.search_start);
8056
0
      components.hash_start = components.search_start;
8057
0
    }
8058
8059
0
    buffer.insert(components.search_start, "?");
8060
0
    size_t idx =
8061
0
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8062
0
    if (idx == input.size()) {
8063
0
      buffer.insert(components.search_start + 1, input);
8064
0
      components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8065
0
    } else {
8066
0
      buffer.insert(components.search_start + 1, input, 0, idx);
8067
0
      input.remove_prefix(idx);
8068
      // We only create a temporary string if we need percent encoding and
8069
      // we attempt to create as small a temporary string as we can.
8070
0
      std::string encoded =
8071
0
          ada::unicode::percent_encode(input, query_percent_encode_set);
8072
0
      buffer.insert(components.search_start + idx + 1, encoded);
8073
0
      components.hash_start +=
8074
0
          uint32_t(encoded.size() + idx + 1);  // Do not forget `?`
8075
0
    }
8076
0
  }
8077
8078
0
  ADA_ASSERT_TRUE(validate());
8079
0
}
8080
8081
2.74k
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8082
2.74k
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8083
2.74k
          " bytes] \n", to_diagram());
8084
2.74k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8085
2.74k
  ADA_ASSERT_TRUE(validate());
8086
8087
2.74k
  const bool begins_with_dashdash = input.starts_with("//");
8088
2.74k
  if (!begins_with_dashdash && has_dash_dot()) {
8089
    // We must delete the ./
8090
0
    delete_dash_dot();
8091
0
  }
8092
8093
2.74k
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8094
0
      !has_dash_dot()) {
8095
    // If url's host is null, url does not have an opaque path, url's path's
8096
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8097
    // output.
8098
0
    buffer.insert(components.pathname_start, "/.");
8099
0
    components.pathname_start += 2;
8100
0
  }
8101
8102
2.74k
  uint32_t difference = replace_and_resize(
8103
2.74k
      components.pathname_start,
8104
2.74k
      components.pathname_start + get_pathname_length(), input);
8105
2.74k
  if (components.search_start != url_components::omitted) {
8106
0
    components.search_start += difference;
8107
0
  }
8108
2.74k
  if (components.hash_start != url_components::omitted) {
8109
0
    components.hash_start += difference;
8110
0
  }
8111
2.74k
  ADA_ASSERT_TRUE(validate());
8112
2.74k
}
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
0
inline void url_aggregator::update_base_username(const std::string_view input) {
8148
0
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8149
0
          "\n", to_diagram());
8150
0
  ADA_ASSERT_TRUE(validate());
8151
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8152
8153
0
  add_authority_slashes_if_needed();
8154
8155
0
  bool has_password = has_non_empty_password();
8156
0
  bool host_starts_with_at = buffer.size() > components.host_start &&
8157
0
                             buffer[components.host_start] == '@';
8158
0
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8159
0
                                     components.username_end, input);
8160
8161
0
  components.username_end += diff;
8162
0
  components.host_start += diff;
8163
8164
0
  if (!input.empty() && !host_starts_with_at) {
8165
0
    buffer.insert(components.host_start, "@");
8166
0
    diff++;
8167
0
  } 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
0
  components.host_end += diff;
8175
0
  components.pathname_start += diff;
8176
0
  if (components.search_start != url_components::omitted) {
8177
0
    components.search_start += diff;
8178
0
  }
8179
0
  if (components.hash_start != url_components::omitted) {
8180
0
    components.hash_start += diff;
8181
0
  }
8182
0
  ADA_ASSERT_TRUE(validate());
8183
0
}
8184
8185
0
inline void url_aggregator::append_base_username(const std::string_view input) {
8186
0
  ada_log("url_aggregator::append_base_username ", input);
8187
0
  ADA_ASSERT_TRUE(validate());
8188
0
  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
0
  add_authority_slashes_if_needed();
8195
8196
  // If input is empty, do nothing.
8197
0
  if (input.empty()) {
8198
0
    return;
8199
0
  }
8200
8201
0
  uint32_t difference = uint32_t(input.size());
8202
0
  buffer.insert(components.username_end, input);
8203
0
  components.username_end += difference;
8204
0
  components.host_start += difference;
8205
8206
0
  if (buffer[components.host_start] != '@' &&
8207
0
      components.host_start != components.host_end) {
8208
0
    buffer.insert(components.host_start, "@");
8209
0
    difference++;
8210
0
  }
8211
8212
0
  components.host_end += difference;
8213
0
  components.pathname_start += difference;
8214
0
  if (components.search_start != url_components::omitted) {
8215
0
    components.search_start += difference;
8216
0
  }
8217
0
  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
0
  ADA_ASSERT_TRUE(validate());
8227
0
}
8228
8229
0
constexpr void url_aggregator::clear_password() {
8230
0
  ada_log("url_aggregator::clear_password ", to_string());
8231
0
  ADA_ASSERT_TRUE(validate());
8232
0
  if (!has_password()) {
8233
0
    return;
8234
0
  }
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
0
inline void url_aggregator::update_base_password(const std::string_view input) {
8250
0
  ada_log("url_aggregator::update_base_password ", input);
8251
0
  ADA_ASSERT_TRUE(validate());
8252
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8253
8254
0
  add_authority_slashes_if_needed();
8255
8256
  // TODO: Optimization opportunity. Merge the following removal functions.
8257
0
  if (input.empty()) {
8258
0
    clear_password();
8259
8260
    // Remove username too, if it is empty.
8261
0
    if (!has_non_empty_username()) {
8262
0
      update_base_username("");
8263
0
    }
8264
8265
0
    return;
8266
0
  }
8267
8268
0
  bool password_exists = has_password();
8269
0
  uint32_t difference = uint32_t(input.size());
8270
8271
0
  if (password_exists) {
8272
0
    uint32_t current_length =
8273
0
        components.host_start - components.username_end - 1;
8274
0
    buffer.erase(components.username_end + 1, current_length);
8275
0
    difference -= current_length;
8276
0
  } else {
8277
0
    buffer.insert(components.username_end, ":");
8278
0
    difference++;
8279
0
  }
8280
8281
0
  buffer.insert(components.username_end + 1, input);
8282
0
  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
0
  if (buffer[components.host_start] != '@') {
8288
0
    buffer.insert(components.host_start, "@");
8289
0
    difference++;
8290
0
  }
8291
8292
0
  components.host_end += difference;
8293
0
  components.pathname_start += difference;
8294
0
  if (components.search_start != url_components::omitted) {
8295
0
    components.search_start += difference;
8296
0
  }
8297
0
  if (components.hash_start != url_components::omitted) {
8298
0
    components.hash_start += difference;
8299
0
  }
8300
0
  ADA_ASSERT_TRUE(validate());
8301
0
}
8302
8303
0
inline void url_aggregator::append_base_password(const std::string_view input) {
8304
0
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8305
0
          "\n", to_diagram());
8306
0
  ADA_ASSERT_TRUE(validate());
8307
0
  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
0
  add_authority_slashes_if_needed();
8314
8315
  // If input is empty, do nothing.
8316
0
  if (input.empty()) {
8317
0
    return;
8318
0
  }
8319
8320
0
  uint32_t difference = uint32_t(input.size());
8321
0
  if (has_password()) {
8322
0
    buffer.insert(components.host_start, input);
8323
0
  } else {
8324
0
    difference++;  // Increment for ":"
8325
0
    buffer.insert(components.username_end, ":");
8326
0
    buffer.insert(components.username_end + 1, input);
8327
0
  }
8328
0
  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
0
  if (buffer[components.host_start] != '@') {
8334
0
    buffer.insert(components.host_start, "@");
8335
0
    difference++;
8336
0
  }
8337
8338
0
  components.host_end += difference;
8339
0
  components.pathname_start += difference;
8340
0
  if (components.search_start != url_components::omitted) {
8341
0
    components.search_start += difference;
8342
0
  }
8343
0
  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
0
  ADA_ASSERT_TRUE(validate());
8353
0
}
8354
8355
0
inline void url_aggregator::update_base_port(uint32_t input) {
8356
0
  ada_log("url_aggregator::update_base_port");
8357
0
  ADA_ASSERT_TRUE(validate());
8358
0
  if (input == url_components::omitted) {
8359
0
    clear_port();
8360
0
    return;
8361
0
  }
8362
  // calling std::to_string(input.value()) is unfortunate given that the port
8363
  // value is probably already available as a string.
8364
0
  std::string value = helpers::concat(":", std::to_string(input));
8365
0
  uint32_t difference = uint32_t(value.size());
8366
8367
0
  if (components.port != url_components::omitted) {
8368
0
    difference -= components.pathname_start - components.host_end;
8369
0
    buffer.erase(components.host_end,
8370
0
                 components.pathname_start - components.host_end);
8371
0
  }
8372
8373
0
  buffer.insert(components.host_end, value);
8374
0
  components.pathname_start += difference;
8375
0
  if (components.search_start != url_components::omitted) {
8376
0
    components.search_start += difference;
8377
0
  }
8378
0
  if (components.hash_start != url_components::omitted) {
8379
0
    components.hash_start += difference;
8380
0
  }
8381
0
  components.port = input;
8382
0
  ADA_ASSERT_TRUE(validate());
8383
0
}
8384
8385
0
inline void url_aggregator::clear_port() {
8386
0
  ada_log("url_aggregator::clear_port");
8387
0
  ADA_ASSERT_TRUE(validate());
8388
0
  if (components.port == url_components::omitted) {
8389
0
    return;
8390
0
  }
8391
0
  uint32_t length = components.pathname_start - components.host_end;
8392
0
  buffer.erase(components.host_end, length);
8393
0
  components.pathname_start -= length;
8394
0
  if (components.search_start != url_components::omitted) {
8395
0
    components.search_start -= length;
8396
0
  }
8397
0
  if (components.hash_start != url_components::omitted) {
8398
0
    components.hash_start -= length;
8399
0
  }
8400
0
  components.port = url_components::omitted;
8401
0
  ADA_ASSERT_TRUE(validate());
8402
0
}
8403
8404
0
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8405
0
  ada_log("url_aggregator::retrieve_base_port");
8406
0
  return components.port;
8407
0
}
8408
8409
0
inline void url_aggregator::clear_search() {
8410
0
  ada_log("url_aggregator::clear_search");
8411
0
  ADA_ASSERT_TRUE(validate());
8412
0
  if (components.search_start == url_components::omitted) {
8413
0
    return;
8414
0
  }
8415
8416
0
  if (components.hash_start == url_components::omitted) {
8417
0
    buffer.resize(components.search_start);
8418
0
  } else {
8419
0
    buffer.erase(components.search_start,
8420
0
                 components.hash_start - components.search_start);
8421
0
    components.hash_start = components.search_start;
8422
0
  }
8423
8424
0
  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
0
  ADA_ASSERT_TRUE(validate());
8432
0
}
8433
8434
0
inline void url_aggregator::clear_hash() {
8435
0
  ada_log("url_aggregator::clear_hash");
8436
0
  ADA_ASSERT_TRUE(validate());
8437
0
  if (components.hash_start == url_components::omitted) {
8438
0
    return;
8439
0
  }
8440
0
  buffer.resize(components.hash_start);
8441
0
  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
0
  ADA_ASSERT_TRUE(validate());
8449
0
}
8450
8451
0
constexpr void url_aggregator::clear_pathname() {
8452
0
  ada_log("url_aggregator::clear_pathname");
8453
0
  ADA_ASSERT_TRUE(validate());
8454
0
  uint32_t ending_index = uint32_t(buffer.size());
8455
0
  if (components.search_start != url_components::omitted) {
8456
0
    ending_index = components.search_start;
8457
0
  } else if (components.hash_start != url_components::omitted) {
8458
0
    ending_index = components.hash_start;
8459
0
  }
8460
0
  uint32_t pathname_length = ending_index - components.pathname_start;
8461
0
  buffer.erase(components.pathname_start, pathname_length);
8462
0
  uint32_t difference = pathname_length;
8463
0
  if (components.pathname_start == components.host_end + 2 &&
8464
0
      buffer[components.host_end] == '/' &&
8465
0
      buffer[components.host_end + 1] == '.') {
8466
0
    components.pathname_start -= 2;
8467
0
    buffer.erase(components.host_end, 2);
8468
0
    difference += 2;
8469
0
  }
8470
0
  if (components.search_start != url_components::omitted) {
8471
0
    components.search_start -= difference;
8472
0
  }
8473
0
  if (components.hash_start != url_components::omitted) {
8474
0
    components.hash_start -= difference;
8475
0
  }
8476
0
  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
0
  ADA_ASSERT_TRUE(validate());
8483
0
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
8484
0
}
8485
8486
0
constexpr void url_aggregator::clear_hostname() {
8487
0
  ada_log("url_aggregator::clear_hostname");
8488
0
  ADA_ASSERT_TRUE(validate());
8489
0
  if (!has_authority()) {
8490
0
    return;
8491
0
  }
8492
0
  ADA_ASSERT_TRUE(has_authority());
8493
8494
0
  uint32_t hostname_length = components.host_end - components.host_start;
8495
0
  uint32_t start = components.host_start;
8496
8497
  // If hostname starts with "@", we should not remove that character.
8498
0
  if (hostname_length > 0 && buffer[start] == '@') {
8499
0
    start++;
8500
0
    hostname_length--;
8501
0
  }
8502
0
  buffer.erase(start, hostname_length);
8503
0
  components.host_end = start;
8504
0
  components.pathname_start -= hostname_length;
8505
0
  if (components.search_start != url_components::omitted) {
8506
0
    components.search_start -= hostname_length;
8507
0
  }
8508
0
  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
0
  ADA_ASSERT_TRUE(has_authority());
8517
0
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
8518
0
                   "hostname should have been cleared on buffer=" + buffer +
8519
0
                       " with " + components.to_string() + "\n" + to_diagram());
8520
0
  ADA_ASSERT_TRUE(validate());
8521
0
}
8522
8523
0
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
8524
0
  ada_log("url_aggregator::has_hash");
8525
0
  return components.hash_start != url_components::omitted;
8526
0
}
8527
8528
0
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
8529
0
  ada_log("url_aggregator::has_search");
8530
0
  return components.search_start != url_components::omitted;
8531
0
}
8532
8533
0
constexpr bool url_aggregator::has_credentials() const noexcept {
8534
0
  ada_log("url_aggregator::has_credentials");
8535
0
  return has_non_empty_username() || has_non_empty_password();
8536
0
}
8537
8538
0
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
8539
0
  ada_log("url_aggregator::cannot_have_credentials_or_port");
8540
0
  return type == ada::scheme::type::FILE ||
8541
0
         components.host_start == components.host_end;
8542
0
}
8543
8544
[[nodiscard]] ada_really_inline const ada::url_components &
8545
0
url_aggregator::get_components() const noexcept {
8546
0
  return components;
8547
0
}
8548
8549
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
8550
2.74k
    const noexcept {
8551
2.74k
  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
2.74k
  return components.protocol_end + 2 <= components.host_start &&
8555
0
         helpers::substring(buffer, components.protocol_end,
8556
0
                            components.protocol_end + 2) == "//";
8557
2.74k
}
8558
8559
2.74k
inline void ada::url_aggregator::add_authority_slashes_if_needed() noexcept {
8560
2.74k
  ada_log("url_aggregator::add_authority_slashes_if_needed");
8561
2.74k
  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
2.74k
  if (has_authority()) {
8566
0
    return;
8567
0
  }
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
2.74k
  buffer.insert(components.protocol_end, "//");
8572
2.74k
  components.username_end += 2;
8573
2.74k
  components.host_start += 2;
8574
2.74k
  components.host_end += 2;
8575
2.74k
  components.pathname_start += 2;
8576
2.74k
  if (components.search_start != url_components::omitted) {
8577
0
    components.search_start += 2;
8578
0
  }
8579
2.74k
  if (components.hash_start != url_components::omitted) {
8580
0
    components.hash_start += 2;
8581
0
  }
8582
2.74k
  ADA_ASSERT_TRUE(validate());
8583
2.74k
}
8584
8585
4.42k
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
8586
4.42k
  buffer.reserve(capacity);
8587
4.42k
}
8588
8589
0
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
8590
0
  ada_log("url_aggregator::has_non_empty_username");
8591
0
  return components.protocol_end + 2 < components.username_end;
8592
0
}
8593
8594
0
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
8595
0
  ada_log("url_aggregator::has_non_empty_password");
8596
0
  return components.host_start - components.username_end > 0;
8597
0
}
8598
8599
0
constexpr bool url_aggregator::has_password() const noexcept {
8600
0
  ada_log("url_aggregator::has_password");
8601
  // This function does not care about the length of the password
8602
0
  return components.host_start > components.username_end &&
8603
0
         buffer[components.username_end] == ':';
8604
0
}
8605
8606
0
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
8607
0
  if (!has_hostname()) {
8608
0
    return false;
8609
0
  }
8610
0
  if (components.host_start == components.host_end) {
8611
0
    return true;
8612
0
  }
8613
0
  if (components.host_end > components.host_start + 1) {
8614
0
    return false;
8615
0
  }
8616
0
  return components.username_end != components.host_start;
8617
0
}
8618
8619
0
constexpr bool url_aggregator::has_hostname() const noexcept {
8620
0
  return has_authority();
8621
0
}
8622
8623
0
constexpr bool url_aggregator::has_port() const noexcept {
8624
0
  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
0
  return has_hostname() && components.pathname_start != components.host_end;
8628
0
}
8629
8630
2.74k
[[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
2.74k
  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
2.74k
  return components.pathname_start == components.host_end + 2 &&
8658
0
         !has_opaque_path && buffer[components.host_end] == '/' &&
8659
0
         buffer[components.host_end + 1] == '.';
8660
2.74k
}
8661
8662
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
8663
0
    const noexcept ada_lifetime_bound {
8664
0
  ada_log("url_aggregator::get_href");
8665
0
  return buffer;
8666
0
}
8667
8668
ada_really_inline size_t url_aggregator::parse_port(
8669
0
    std::string_view view, bool check_trailing_content) noexcept {
8670
0
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
8671
0
  if (!view.empty() && view[0] == '-') {
8672
0
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
8673
0
    is_valid = false;
8674
0
    return 0;
8675
0
  }
8676
0
  uint16_t parsed_port{};
8677
0
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
8678
0
  if (r.ec == std::errc::result_out_of_range) {
8679
0
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
8680
0
    is_valid = false;
8681
0
    return 0;
8682
0
  }
8683
0
  ada_log("parse_port: ", parsed_port);
8684
0
  const size_t consumed = size_t(r.ptr - view.data());
8685
0
  ada_log("parse_port: consumed ", consumed);
8686
0
  if (check_trailing_content) {
8687
0
    is_valid &=
8688
0
        (consumed == view.size() || view[consumed] == '/' ||
8689
0
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
8690
0
  }
8691
0
  ada_log("parse_port: is_valid = ", is_valid);
8692
0
  if (is_valid) {
8693
0
    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
0
    auto default_port = scheme_default_port();
8696
0
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
8697
0
                         (default_port != parsed_port);
8698
0
    if (r.ec == std::errc() && is_port_valid) {
8699
0
      update_base_port(parsed_port);
8700
0
    } else {
8701
0
      clear_port();
8702
0
    }
8703
0
  }
8704
0
  return consumed;
8705
0
}
8706
8707
0
constexpr void url_aggregator::set_protocol_as_file() {
8708
0
  ada_log("url_aggregator::set_protocol_as_file ");
8709
0
  ADA_ASSERT_TRUE(validate());
8710
0
  type = ada::scheme::type::FILE;
8711
  // next line could overflow but unsigned arithmetic has well-defined
8712
  // overflows.
8713
0
  uint32_t new_difference = 5 - components.protocol_end;
8714
8715
0
  if (buffer.empty()) {
8716
0
    buffer.append("file:");
8717
0
  } else {
8718
0
    buffer.erase(0, components.protocol_end);
8719
0
    buffer.insert(0, "file:");
8720
0
  }
8721
0
  components.protocol_end = 5;
8722
8723
  // Update the rest of the components.
8724
0
  components.username_end += new_difference;
8725
0
  components.host_start += new_difference;
8726
0
  components.host_end += new_difference;
8727
0
  components.pathname_start += new_difference;
8728
0
  if (components.search_start != url_components::omitted) {
8729
0
    components.search_start += new_difference;
8730
0
  }
8731
0
  if (components.hash_start != url_components::omitted) {
8732
0
    components.hash_start += new_difference;
8733
0
  }
8734
0
  ADA_ASSERT_TRUE(validate());
8735
0
}
8736
8737
0
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
8738
0
  if (!is_valid) {
8739
0
    return true;
8740
0
  }
8741
0
  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
0
  // We have a credible components struct, but let us investivate more
8747
0
  // carefully:
8748
0
  /**
8749
0
   * https://user:pass@example.com:1234/foo/bar?baz#quux
8750
0
   *       |     |    |          | ^^^^|       |   |
8751
0
   *       |     |    |          | |   |       |   `----- hash_start
8752
0
   *       |     |    |          | |   |       `--------- search_start
8753
0
   *       |     |    |          | |   `----------------- pathname_start
8754
0
   *       |     |    |          | `--------------------- port
8755
0
   *       |     |    |          `----------------------- host_end
8756
0
   *       |     |    `---------------------------------- host_start
8757
0
   *       |     `--------------------------------------- username_end
8758
0
   *       `--------------------------------------------- protocol_end
8759
0
   */
8760
0
  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
0
  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
0
  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
0
  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
0
  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
0
8781
0
  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
0
  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
0
  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
0
  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
0
  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
0
8803
0
  if (components.protocol_end > 0) {
8804
0
    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
0
  }
8811
0
8812
0
  if (components.username_end != buffer.size() &&
8813
0
      components.username_end > components.protocol_end + 2) {
8814
0
    if (buffer[components.username_end] != ':' &&
8815
0
        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
0
  }
8823
0
8824
0
  if (components.host_start != buffer.size()) {
8825
0
    if (components.host_start > components.username_end) {
8826
0
      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
0
    } else if (components.host_start == components.username_end &&
8833
0
               components.host_end > components.host_start) {
8834
0
      if (components.host_start == components.protocol_end + 2) {
8835
0
        if (buffer[components.protocol_end] != '/' ||
8836
0
            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
0
      } else {
8844
0
        if (components.host_start > components.protocol_end &&
8845
0
            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
0
      }
8853
0
    } else {
8854
0
      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
0
    }
8860
0
  }
8861
0
  if (components.host_end != buffer.size() &&
8862
0
      components.pathname_start > components.host_end) {
8863
0
    if (components.pathname_start == components.host_end + 2 &&
8864
0
        buffer[components.host_end] == '/' &&
8865
0
        buffer[components.host_end + 1] == '.') {
8866
0
      if (components.pathname_start + 1 >= buffer.size() ||
8867
0
          buffer[components.pathname_start] != '/' ||
8868
0
          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
0
    } 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
0
  }
8880
0
  if (components.pathname_start != buffer.size() &&
8881
0
      components.pathname_start < components.search_start &&
8882
0
      components.pathname_start < components.hash_start && !has_opaque_path) {
8883
0
    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
0
  }
8889
0
  if (components.search_start != url_components::omitted) {
8890
0
    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
0
  }
8896
0
  if (components.hash_start != url_components::omitted) {
8897
0
    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
0
  }
8903
0
8904
0
  return true;
8905
0
}
8906
8907
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname()
8908
0
    const noexcept ada_lifetime_bound {
8909
0
  ada_log("url_aggregator::get_pathname pathname_start = ",
8910
0
          components.pathname_start, " buffer.size() = ", buffer.size(),
8911
0
          " components.search_start = ", components.search_start,
8912
0
          " components.hash_start = ", components.hash_start);
8913
0
  auto ending_index = uint32_t(buffer.size());
8914
0
  if (components.search_start != url_components::omitted) {
8915
0
    ending_index = components.search_start;
8916
0
  } else if (components.hash_start != url_components::omitted) {
8917
0
    ending_index = components.hash_start;
8918
0
  }
8919
0
  return helpers::substring(buffer, components.pathname_start, ending_index);
8920
0
}
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
void url_aggregator::update_host_to_base_host(
8928
0
    const std::string_view input) noexcept {
8929
0
  ada_log("url_aggregator::update_host_to_base_host ", input);
8930
0
  ADA_ASSERT_TRUE(validate());
8931
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8932
0
  if (type != ada::scheme::type::FILE) {
8933
    // Let host be the result of host parsing host_view with url is not special.
8934
0
    if (input.empty() && !is_special()) {
8935
0
      if (has_hostname()) {
8936
0
        clear_hostname();
8937
0
      } else if (has_dash_dot()) {
8938
0
        add_authority_slashes_if_needed();
8939
0
        delete_dash_dot();
8940
0
      }
8941
0
      return;
8942
0
    }
8943
0
  }
8944
0
  update_base_hostname(input);
8945
0
  ADA_ASSERT_TRUE(validate());
8946
0
  return;
8947
0
}
8948
}  // namespace ada
8949
8950
#endif  // ADA_URL_AGGREGATOR_INL_H
8951
/* end file include/ada/url_aggregator-inl.h */
8952
/* begin file include/ada/url_search_params.h */
8953
/**
8954
 * @file url_search_params.h
8955
 * @brief URL query string parameter manipulation.
8956
 *
8957
 * This file provides the `url_search_params` class for parsing, manipulating,
8958
 * and serializing URL query strings. It implements the URLSearchParams API
8959
 * from the WHATWG URL Standard.
8960
 *
8961
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
8962
 */
8963
#ifndef ADA_URL_SEARCH_PARAMS_H
8964
#define ADA_URL_SEARCH_PARAMS_H
8965
8966
#include <optional>
8967
#include <string>
8968
#include <string_view>
8969
#include <vector>
8970
8971
namespace ada {
8972
8973
/**
8974
 * @brief Iterator types for url_search_params iteration.
8975
 */
8976
enum class url_search_params_iter_type {
8977
  KEYS,    /**< Iterate over parameter keys only */
8978
  VALUES,  /**< Iterate over parameter values only */
8979
  ENTRIES, /**< Iterate over key-value pairs */
8980
};
8981
8982
template <typename T, url_search_params_iter_type Type>
8983
struct url_search_params_iter;
8984
8985
/** Type alias for a key-value pair of string views. */
8986
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
8987
8988
/** Iterator over search parameter keys. */
8989
using url_search_params_keys_iter =
8990
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
8991
/** Iterator over search parameter values. */
8992
using url_search_params_values_iter =
8993
    url_search_params_iter<std::string_view,
8994
                           url_search_params_iter_type::VALUES>;
8995
/** Iterator over search parameter key-value pairs. */
8996
using url_search_params_entries_iter =
8997
    url_search_params_iter<key_value_view_pair,
8998
                           url_search_params_iter_type::ENTRIES>;
8999
9000
/**
9001
 * @brief Class for parsing and manipulating URL query strings.
9002
 *
9003
 * The `url_search_params` class provides methods to parse, modify, and
9004
 * serialize URL query parameters (the part after '?' in a URL). It handles
9005
 * percent-encoding and decoding automatically.
9006
 *
9007
 * All string inputs must be valid UTF-8. The caller is responsible for
9008
 * ensuring UTF-8 validity.
9009
 *
9010
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9011
 */
9012
struct url_search_params {
9013
  url_search_params() = default;
9014
9015
  /**
9016
   * Constructs url_search_params by parsing a query string.
9017
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9018
   */
9019
0
  explicit url_search_params(const std::string_view input) {
9020
0
    initialize(input);
9021
0
  }
9022
9023
  url_search_params(const url_search_params &u) = default;
9024
0
  url_search_params(url_search_params &&u) noexcept = default;
9025
  url_search_params &operator=(url_search_params &&u) noexcept = default;
9026
  url_search_params &operator=(const url_search_params &u) = default;
9027
0
  ~url_search_params() = default;
9028
9029
  /**
9030
   * Returns the number of key-value pairs.
9031
   * @return The total count of parameters.
9032
   */
9033
  [[nodiscard]] inline size_t size() const noexcept;
9034
9035
  /**
9036
   * Appends a new key-value pair to the parameter list.
9037
   * @param key The parameter name (must be valid UTF-8).
9038
   * @param value The parameter value (must be valid UTF-8).
9039
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9040
   */
9041
  inline void append(std::string_view key, std::string_view value);
9042
9043
  /**
9044
   * Removes all pairs with the given key.
9045
   * @param key The parameter name to remove.
9046
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9047
   */
9048
  inline void remove(std::string_view key);
9049
9050
  /**
9051
   * Removes all pairs with the given key and value.
9052
   * @param key The parameter name.
9053
   * @param value The parameter value to match.
9054
   */
9055
  inline void remove(std::string_view key, std::string_view value);
9056
9057
  /**
9058
   * Returns the value of the first pair with the given key.
9059
   * @param key The parameter name to search for.
9060
   * @return The value if found, or std::nullopt if not present.
9061
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9062
   */
9063
  inline std::optional<std::string_view> get(std::string_view key);
9064
9065
  /**
9066
   * Returns all values for pairs with the given key.
9067
   * @param key The parameter name to search for.
9068
   * @return A vector of all matching values (may be empty).
9069
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9070
   */
9071
  inline std::vector<std::string> get_all(std::string_view key);
9072
9073
  /**
9074
   * Checks if any pair has the given key.
9075
   * @param key The parameter name to search for.
9076
   * @return `true` if at least one pair has this key.
9077
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9078
   */
9079
  inline bool has(std::string_view key) noexcept;
9080
9081
  /**
9082
   * Checks if any pair matches the given key and value.
9083
   * @param key The parameter name to search for.
9084
   * @param value The parameter value to match.
9085
   * @return `true` if a matching pair exists.
9086
   */
9087
  inline bool has(std::string_view key, std::string_view value) noexcept;
9088
9089
  /**
9090
   * Sets a parameter value, replacing any existing pairs with the same key.
9091
   * @param key The parameter name (must be valid UTF-8).
9092
   * @param value The parameter value (must be valid UTF-8).
9093
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9094
   */
9095
  inline void set(std::string_view key, std::string_view value);
9096
9097
  /**
9098
   * Sorts all key-value pairs by their keys using code unit comparison.
9099
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9100
   */
9101
  inline void sort();
9102
9103
  /**
9104
   * Serializes the parameters to a query string (without leading '?').
9105
   * @return The percent-encoded query string.
9106
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9107
   */
9108
  inline std::string to_string() const;
9109
9110
  /**
9111
   * Returns an iterator over all parameter keys.
9112
   * Keys may repeat if there are duplicate parameters.
9113
   * @return An iterator yielding string_view keys.
9114
   * @note The iterator is invalidated if this object is modified.
9115
   */
9116
  inline url_search_params_keys_iter get_keys();
9117
9118
  /**
9119
   * Returns an iterator over all parameter values.
9120
   * @return An iterator yielding string_view values.
9121
   * @note The iterator is invalidated if this object is modified.
9122
   */
9123
  inline url_search_params_values_iter get_values();
9124
9125
  /**
9126
   * Returns an iterator over all key-value pairs.
9127
   * @return An iterator yielding key-value pair views.
9128
   * @note The iterator is invalidated if this object is modified.
9129
   */
9130
  inline url_search_params_entries_iter get_entries();
9131
9132
  /**
9133
   * C++ style conventional iterator support. const only because we
9134
   * do not really want the params to be modified via the iterator.
9135
   */
9136
0
  inline auto begin() const { return params.begin(); }
9137
0
  inline auto end() const { return params.end(); }
9138
0
  inline auto front() const { return params.front(); }
9139
0
  inline auto back() const { return params.back(); }
9140
0
  inline auto operator[](size_t index) const { return params[index]; }
9141
9142
  /**
9143
   * @private
9144
   * Used to reset the search params to a new input.
9145
   * Used primarily for C API.
9146
   * @param input
9147
   */
9148
  void reset(std::string_view input);
9149
9150
 private:
9151
  typedef std::pair<std::string, std::string> key_value_pair;
9152
  std::vector<key_value_pair> params{};
9153
9154
  /**
9155
   * The init parameter must be valid UTF-8.
9156
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9157
   */
9158
  void initialize(std::string_view init);
9159
9160
  template <typename T, url_search_params_iter_type Type>
9161
  friend struct url_search_params_iter;
9162
};  // url_search_params
9163
9164
/**
9165
 * @brief JavaScript-style iterator for url_search_params.
9166
 *
9167
 * Provides a `next()` method that returns successive values until exhausted.
9168
 * This matches the iterator pattern used in the Web Platform.
9169
 *
9170
 * @tparam T The type of value returned by the iterator.
9171
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9172
 *
9173
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9174
 */
9175
template <typename T, url_search_params_iter_type Type>
9176
struct url_search_params_iter {
9177
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()
9178
  url_search_params_iter(const url_search_params_iter &u) = default;
9179
  url_search_params_iter(url_search_params_iter &&u) noexcept = default;
9180
  url_search_params_iter &operator=(url_search_params_iter &&u) noexcept =
9181
      default;
9182
  url_search_params_iter &operator=(const url_search_params_iter &u) = default;
9183
  ~url_search_params_iter() = default;
9184
9185
  /**
9186
   * Returns the next value in the iteration sequence.
9187
   * @return The next value, or std::nullopt if iteration is complete.
9188
   */
9189
  inline std::optional<T> next();
9190
9191
  /**
9192
   * Checks if more values are available.
9193
   * @return `true` if `next()` will return a value, `false` if exhausted.
9194
   */
9195
  inline bool has_next() const;
9196
9197
 private:
9198
  static url_search_params EMPTY;
9199
0
  inline url_search_params_iter(url_search_params &params_) : params(params_) {}
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::url_search_params_iter(ada::url_search_params&)
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::url_search_params_iter(ada::url_search_params&)
Unexecuted instantiation: ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::url_search_params_iter(ada::url_search_params&)
9200
9201
  url_search_params &params;
9202
  size_t pos = 0;
9203
9204
  friend struct url_search_params;
9205
};
9206
9207
}  // namespace ada
9208
#endif
9209
/* end file include/ada/url_search_params.h */
9210
/* begin file include/ada/url_search_params-inl.h */
9211
/**
9212
 * @file url_search_params-inl.h
9213
 * @brief Inline declarations for the URL Search Params
9214
 */
9215
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9216
#define ADA_URL_SEARCH_PARAMS_INL_H
9217
9218
9219
#include <algorithm>
9220
#include <optional>
9221
#include <ranges>
9222
#include <string>
9223
#include <string_view>
9224
#include <vector>
9225
9226
namespace ada {
9227
9228
// A default, empty url_search_params for use with empty iterators.
9229
template <typename T, ada::url_search_params_iter_type Type>
9230
url_search_params url_search_params_iter<T, Type>::EMPTY;
9231
9232
0
inline void url_search_params::reset(std::string_view input) {
9233
0
  params.clear();
9234
0
  initialize(input);
9235
0
}
9236
9237
0
inline void url_search_params::initialize(std::string_view input) {
9238
0
  if (!input.empty() && input.front() == '?') {
9239
0
    input.remove_prefix(1);
9240
0
  }
9241
9242
0
  auto process_key_value = [&](const std::string_view current) {
9243
0
    auto equal = current.find('=');
9244
9245
0
    if (equal == std::string_view::npos) {
9246
0
      std::string name(current);
9247
0
      std::ranges::replace(name, '+', ' ');
9248
0
      params.emplace_back(unicode::percent_decode(name, name.find('%')), "");
9249
0
    } else {
9250
0
      std::string name(current.substr(0, equal));
9251
0
      std::string value(current.substr(equal + 1));
9252
9253
0
      std::ranges::replace(name, '+', ' ');
9254
0
      std::ranges::replace(value, '+', ' ');
9255
9256
0
      params.emplace_back(unicode::percent_decode(name, name.find('%')),
9257
0
                          unicode::percent_decode(value, value.find('%')));
9258
0
    }
9259
0
  };
9260
9261
0
  while (!input.empty()) {
9262
0
    auto ampersand_index = input.find('&');
9263
9264
0
    if (ampersand_index == std::string_view::npos) {
9265
0
      if (!input.empty()) {
9266
0
        process_key_value(input);
9267
0
      }
9268
0
      break;
9269
0
    } else if (ampersand_index != 0) {
9270
0
      process_key_value(input.substr(0, ampersand_index));
9271
0
    }
9272
9273
0
    input.remove_prefix(ampersand_index + 1);
9274
0
  }
9275
0
}
9276
9277
inline void url_search_params::append(const std::string_view key,
9278
0
                                      const std::string_view value) {
9279
0
  params.emplace_back(key, value);
9280
0
}
9281
9282
0
inline size_t url_search_params::size() const noexcept { return params.size(); }
9283
9284
inline std::optional<std::string_view> url_search_params::get(
9285
0
    const std::string_view key) {
9286
0
  auto entry = std::ranges::find_if(
9287
0
      params, [&key](const auto &param) { return param.first == key; });
9288
9289
0
  if (entry == params.end()) {
9290
0
    return std::nullopt;
9291
0
  }
9292
9293
0
  return entry->second;
9294
0
}
9295
9296
inline std::vector<std::string> url_search_params::get_all(
9297
0
    const std::string_view key) {
9298
0
  std::vector<std::string> out{};
9299
9300
0
  for (auto &param : params) {
9301
0
    if (param.first == key) {
9302
0
      out.emplace_back(param.second);
9303
0
    }
9304
0
  }
9305
9306
0
  return out;
9307
0
}
9308
9309
0
inline bool url_search_params::has(const std::string_view key) noexcept {
9310
0
  auto entry = std::ranges::find_if(
9311
0
      params, [&key](const auto &param) { return param.first == key; });
9312
0
  return entry != params.end();
9313
0
}
9314
9315
inline bool url_search_params::has(std::string_view key,
9316
0
                                   std::string_view value) noexcept {
9317
0
  auto entry = std::ranges::find_if(params, [&key, &value](const auto &param) {
9318
0
    return param.first == key && param.second == value;
9319
0
  });
9320
0
  return entry != params.end();
9321
0
}
9322
9323
0
inline std::string url_search_params::to_string() const {
9324
0
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9325
0
  std::string out{};
9326
0
  for (size_t i = 0; i < params.size(); i++) {
9327
0
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9328
0
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9329
9330
    // Performance optimization: Move this inside percent_encode.
9331
0
    std::ranges::replace(key, ' ', '+');
9332
0
    std::ranges::replace(value, ' ', '+');
9333
9334
0
    if (i != 0) {
9335
0
      out += "&";
9336
0
    }
9337
0
    out.append(key);
9338
0
    out += "=";
9339
0
    out.append(value);
9340
0
  }
9341
0
  return out;
9342
0
}
9343
9344
inline void url_search_params::set(const std::string_view key,
9345
0
                                   const std::string_view value) {
9346
0
  const auto find = [&key](const auto &param) { return param.first == key; };
9347
9348
0
  auto it = std::ranges::find_if(params, find);
9349
9350
0
  if (it == params.end()) {
9351
0
    params.emplace_back(key, value);
9352
0
  } else {
9353
0
    it->second = value;
9354
0
    params.erase(std::remove_if(std::next(it), params.end(), find),
9355
0
                 params.end());
9356
0
  }
9357
0
}
9358
9359
0
inline void url_search_params::remove(const std::string_view key) {
9360
0
  std::erase_if(params,
9361
0
                [&key](const auto &param) { return param.first == key; });
9362
0
}
9363
9364
inline void url_search_params::remove(const std::string_view key,
9365
0
                                      const std::string_view value) {
9366
0
  std::erase_if(params, [&key, &value](const auto &param) {
9367
0
    return param.first == key && param.second == value;
9368
0
  });
9369
0
}
9370
9371
0
inline void url_search_params::sort() {
9372
  // We rely on the fact that the content is valid UTF-8.
9373
0
  std::ranges::stable_sort(params, [](const key_value_pair &lhs,
9374
0
                                      const key_value_pair &rhs) {
9375
0
    size_t i = 0, j = 0;
9376
0
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9377
0
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9378
0
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9379
0
      uint32_t codePoint1 = 0, codePoint2 = 0;
9380
9381
0
      if (low_surrogate1 != 0) {
9382
0
        codePoint1 = low_surrogate1;
9383
0
        low_surrogate1 = 0;
9384
0
      } else {
9385
0
        uint8_t c1 = uint8_t(lhs.first[i]);
9386
0
        if (c1 <= 0x7F) {
9387
0
          codePoint1 = c1;
9388
0
          i++;
9389
0
        } else if (c1 <= 0xDF) {
9390
0
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9391
0
          i += 2;
9392
0
        } else if (c1 <= 0xEF) {
9393
0
          codePoint1 = ((c1 & 0x0F) << 12) |
9394
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9395
0
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9396
0
          i += 3;
9397
0
        } else {
9398
0
          codePoint1 = ((c1 & 0x07) << 18) |
9399
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9400
0
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9401
0
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9402
0
          i += 4;
9403
9404
0
          codePoint1 -= 0x10000;
9405
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9406
0
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9407
0
          codePoint1 = high_surrogate;
9408
0
        }
9409
0
      }
9410
9411
0
      if (low_surrogate2 != 0) {
9412
0
        codePoint2 = low_surrogate2;
9413
0
        low_surrogate2 = 0;
9414
0
      } else {
9415
0
        uint8_t c2 = uint8_t(rhs.first[j]);
9416
0
        if (c2 <= 0x7F) {
9417
0
          codePoint2 = c2;
9418
0
          j++;
9419
0
        } else if (c2 <= 0xDF) {
9420
0
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
9421
0
          j += 2;
9422
0
        } else if (c2 <= 0xEF) {
9423
0
          codePoint2 = ((c2 & 0x0F) << 12) |
9424
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
9425
0
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
9426
0
          j += 3;
9427
0
        } else {
9428
0
          codePoint2 = ((c2 & 0x07) << 18) |
9429
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
9430
0
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
9431
0
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
9432
0
          j += 4;
9433
0
          codePoint2 -= 0x10000;
9434
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
9435
0
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
9436
0
          codePoint2 = high_surrogate;
9437
0
        }
9438
0
      }
9439
9440
0
      if (codePoint1 != codePoint2) {
9441
0
        return (codePoint1 < codePoint2);
9442
0
      }
9443
0
    }
9444
0
    return (j < rhs.first.size() || low_surrogate2 != 0);
9445
0
  });
9446
0
}
9447
9448
0
inline url_search_params_keys_iter url_search_params::get_keys() {
9449
0
  return url_search_params_keys_iter(*this);
9450
0
}
9451
9452
/**
9453
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9454
 */
9455
0
inline url_search_params_values_iter url_search_params::get_values() {
9456
0
  return url_search_params_values_iter(*this);
9457
0
}
9458
9459
/**
9460
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9461
 */
9462
0
inline url_search_params_entries_iter url_search_params::get_entries() {
9463
0
  return url_search_params_entries_iter(*this);
9464
0
}
9465
9466
template <typename T, url_search_params_iter_type Type>
9467
0
inline bool url_search_params_iter<T, Type>::has_next() const {
9468
0
  return pos < params.params.size();
9469
0
}
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::has_next() const
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::has_next() const
Unexecuted instantiation: ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::has_next() const
9470
9471
template <>
9472
0
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
9473
0
  if (!has_next()) {
9474
0
    return std::nullopt;
9475
0
  }
9476
0
  return params.params[pos++].first;
9477
0
}
9478
9479
template <>
9480
0
inline std::optional<std::string_view> url_search_params_values_iter::next() {
9481
0
  if (!has_next()) {
9482
0
    return std::nullopt;
9483
0
  }
9484
0
  return params.params[pos++].second;
9485
0
}
9486
9487
template <>
9488
inline std::optional<key_value_view_pair>
9489
0
url_search_params_entries_iter::next() {
9490
0
  if (!has_next()) {
9491
0
    return std::nullopt;
9492
0
  }
9493
0
  return params.params[pos++];
9494
0
}
9495
9496
}  // namespace ada
9497
9498
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
9499
/* end file include/ada/url_search_params-inl.h */
9500
9501
/* begin file include/ada/url_pattern-inl.h */
9502
/**
9503
 * @file url_pattern-inl.h
9504
 * @brief Declaration for the URLPattern inline functions.
9505
 */
9506
#ifndef ADA_URL_PATTERN_INL_H
9507
#define ADA_URL_PATTERN_INL_H
9508
9509
9510
#include <algorithm>
9511
#include <string_view>
9512
#include <utility>
9513
9514
#if ADA_INCLUDE_URL_PATTERN
9515
namespace ada {
9516
9517
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
9518
0
  return protocol == other.protocol && username == other.username &&
9519
0
         password == other.password && hostname == other.hostname &&
9520
0
         port == other.port && search == other.search && hash == other.hash &&
9521
0
         pathname == other.pathname;
9522
0
}
9523
9524
inline bool url_pattern_component_result::operator==(
9525
0
    const url_pattern_component_result& other) const {
9526
0
  return input == other.input && groups == other.groups;
9527
0
}
9528
9529
template <url_pattern_regex::regex_concept regex_provider>
9530
url_pattern_component_result
9531
url_pattern_component<regex_provider>::create_component_match_result(
9532
    std::string&& input,
9533
    std::vector<std::optional<std::string>>&& exec_result) {
9534
  // Let result be a new URLPatternComponentResult.
9535
  // Set result["input"] to input.
9536
  // Let groups be a record<USVString, (USVString or undefined)>.
9537
  auto result =
9538
      url_pattern_component_result{.input = std::move(input), .groups = {}};
9539
9540
  // Optimization: Let's reserve the size.
9541
  result.groups.reserve(exec_result.size());
9542
9543
  // We explicitly start iterating from 0 even though the spec
9544
  // says we should start from 1. This case is handled by the
9545
  // std_regex_provider.
9546
  for (size_t index = 0; index < exec_result.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.11k
    ada_lifetime_bound {
9556
  // Return this's associated URL pattern's protocol component's pattern string.
9557
4.11k
  return protocol_component.pattern;
9558
4.11k
}
9559
template <url_pattern_regex::regex_concept regex_provider>
9560
std::string_view url_pattern<regex_provider>::get_username() const
9561
4.11k
    ada_lifetime_bound {
9562
  // Return this's associated URL pattern's username component's pattern string.
9563
4.11k
  return username_component.pattern;
9564
4.11k
}
9565
template <url_pattern_regex::regex_concept regex_provider>
9566
std::string_view url_pattern<regex_provider>::get_password() const
9567
4.11k
    ada_lifetime_bound {
9568
  // Return this's associated URL pattern's password component's pattern string.
9569
4.11k
  return password_component.pattern;
9570
4.11k
}
9571
template <url_pattern_regex::regex_concept regex_provider>
9572
std::string_view url_pattern<regex_provider>::get_hostname() const
9573
4.11k
    ada_lifetime_bound {
9574
  // Return this's associated URL pattern's hostname component's pattern string.
9575
4.11k
  return hostname_component.pattern;
9576
4.11k
}
9577
template <url_pattern_regex::regex_concept regex_provider>
9578
std::string_view url_pattern<regex_provider>::get_port() const
9579
4.11k
    ada_lifetime_bound {
9580
  // Return this's associated URL pattern's port component's pattern string.
9581
4.11k
  return port_component.pattern;
9582
4.11k
}
9583
template <url_pattern_regex::regex_concept regex_provider>
9584
std::string_view url_pattern<regex_provider>::get_pathname() const
9585
4.11k
    ada_lifetime_bound {
9586
  // Return this's associated URL pattern's pathname component's pattern string.
9587
4.11k
  return pathname_component.pattern;
9588
4.11k
}
9589
template <url_pattern_regex::regex_concept regex_provider>
9590
std::string_view url_pattern<regex_provider>::get_search() const
9591
4.11k
    ada_lifetime_bound {
9592
  // Return this's associated URL pattern's search component's pattern string.
9593
4.11k
  return search_component.pattern;
9594
4.11k
}
9595
template <url_pattern_regex::regex_concept regex_provider>
9596
std::string_view url_pattern<regex_provider>::get_hash() const
9597
4.11k
    ada_lifetime_bound {
9598
  // Return this's associated URL pattern's hash component's pattern string.
9599
4.11k
  return hash_component.pattern;
9600
4.11k
}
9601
template <url_pattern_regex::regex_concept regex_provider>
9602
4.11k
bool url_pattern<regex_provider>::ignore_case() const {
9603
4.11k
  return ignore_case_;
9604
4.11k
}
9605
template <url_pattern_regex::regex_concept regex_provider>
9606
4.11k
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.11k
  return protocol_component.has_regexp_groups ||
9609
4.11k
         username_component.has_regexp_groups ||
9610
4.11k
         password_component.has_regexp_groups ||
9611
4.11k
         hostname_component.has_regexp_groups ||
9612
4.11k
         port_component.has_regexp_groups ||
9613
4.11k
         pathname_component.has_regexp_groups ||
9614
4.11k
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
9615
4.11k
}
9616
9617
32.9k
inline bool url_pattern_part::is_regexp() const noexcept {
9618
32.9k
  return type == url_pattern_part_type::REGEXP;
9619
32.9k
}
9620
9621
inline std::string_view url_pattern_compile_component_options::get_delimiter()
9622
38.6k
    const {
9623
38.6k
  if (delimiter) {
9624
8.23k
    return {&delimiter.value(), 1};
9625
8.23k
  }
9626
30.4k
  return {};
9627
38.6k
}
9628
9629
inline std::string_view url_pattern_compile_component_options::get_prefix()
9630
8.70k
    const {
9631
8.70k
  if (prefix) {
9632
8.23k
    return {&prefix.value(), 1};
9633
8.23k
  }
9634
468
  return {};
9635
8.70k
}
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
38.6k
    url_pattern_compile_component_options& options) {
9643
38.6k
  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
38.6k
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
9647
38.6k
                                                             encoding_callback);
9648
9649
38.6k
  if (!part_list) {
9650
1.59k
    ada_log("parse_pattern_string failed");
9651
1.59k
    return tl::unexpected(part_list.error());
9652
1.59k
  }
9653
9654
  // Detect pattern type early to potentially skip expensive regex compilation
9655
37.0k
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
9656
37.0k
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
9657
9658
37.0k
  url_pattern_component_type component_type =
9659
37.0k
      url_pattern_component_type::REGEXP;
9660
37.0k
  std::string exact_match_value{};
9661
9662
37.0k
  if (part_list->empty()) {
9663
4.11k
    component_type = url_pattern_component_type::EMPTY;
9664
32.9k
  } else if (part_list->size() == 1) {
9665
32.9k
    const auto& part = (*part_list)[0];
9666
32.9k
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
9667
12.3k
        part.modifier == url_pattern_part_modifier::none &&
9668
12.3k
        !options.ignore_case) {
9669
12.3k
      component_type = url_pattern_component_type::EXACT_MATCH;
9670
12.3k
      exact_match_value = part.value;
9671
20.5k
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
9672
20.5k
               part.modifier == url_pattern_part_modifier::none &&
9673
20.5k
               part.prefix.empty() && part.suffix.empty()) {
9674
16.4k
      component_type = url_pattern_component_type::FULL_WILDCARD;
9675
16.4k
    }
9676
32.9k
  }
9677
9678
  // For simple patterns, skip regex generation and compilation entirely
9679
37.0k
  if (component_type != url_pattern_component_type::REGEXP) {
9680
32.9k
    auto pattern_string =
9681
32.9k
        url_pattern_helpers::generate_pattern_string(*part_list, options);
9682
    // For FULL_WILDCARD, we need the group name from
9683
    // generate_regular_expression
9684
32.9k
    std::vector<std::string> name_list;
9685
32.9k
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
9686
16.4k
        !part_list->empty()) {
9687
16.4k
      name_list.push_back((*part_list)[0].name);
9688
16.4k
    }
9689
32.9k
    return url_pattern_component<regex_provider>(
9690
32.9k
        std::move(pattern_string), typename regex_provider::regex_type{},
9691
32.9k
        std::move(name_list), has_regexp_groups, component_type,
9692
32.9k
        std::move(exact_match_value));
9693
32.9k
  }
9694
9695
  // Generate regex for complex patterns
9696
4.11k
  auto [regular_expression_string, name_list] =
9697
4.11k
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
9698
4.11k
                                                                     options);
9699
4.11k
  auto pattern_string =
9700
4.11k
      url_pattern_helpers::generate_pattern_string(*part_list, options);
9701
9702
4.11k
  std::optional<typename regex_provider::regex_type> regular_expression =
9703
4.11k
      regex_provider::create_instance(regular_expression_string,
9704
4.11k
                                      options.ignore_case);
9705
4.11k
  if (!regular_expression) {
9706
0
    return tl::unexpected(errors::type_error);
9707
0
  }
9708
9709
4.11k
  return url_pattern_component<regex_provider>(
9710
4.11k
      std::move(pattern_string), std::move(*regular_expression),
9711
4.11k
      std::move(name_list), has_regexp_groups, component_type,
9712
4.11k
      std::move(exact_match_value));
9713
4.11k
}
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.3k
constexpr void constructor_string_parser<regex_provider>::rewind() {
10156
  // Set parser's token index to parser's component start.
10157
12.3k
  token_index = component_start;
10158
  // Set parser's token increment to 0.
10159
12.3k
  token_increment = 0;
10160
12.3k
}
10161
10162
template <url_pattern_regex::regex_concept regex_provider>
10163
121k
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
121k
  return is_non_special_pattern_char(token_index, '#');
10167
121k
}
10168
10169
template <url_pattern_regex::regex_concept regex_provider>
10170
117k
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
117k
  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
117k
  if (token_list[token_index].value != "?") {
10180
116k
    return false;
10181
116k
  }
10182
10183
  // If previous index is less than 0, then return true.
10184
1.16k
  if (token_index == 0) return true;
10185
  // Let previous index be parser's token index - 1.
10186
1.16k
  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.16k
  auto previous_token = get_safe_token(previous_index);
10190
1.16k
  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.16k
  return !(previous_token->type == token_type::NAME ||
10197
1.04k
           previous_token->type == token_type::REGEXP ||
10198
924
           previous_token->type == token_type::CLOSE ||
10199
636
           previous_token->type == token_type::ASTERISK);
10200
1.16k
}
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
670k
    size_t index, uint32_t value) const {
10206
  // Let token be the result of running get a safe token given parser and index.
10207
670k
  auto token = get_safe_token(index);
10208
670k
  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
670k
  if (!token->value.empty() &&
10214
670k
      static_cast<uint32_t>(token->value[0]) != value) {
10215
640k
    return false;
10216
640k
  }
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
30.2k
  return token->type == token_type::CHAR ||
10224
13.6k
         token->type == token_type::ESCAPED_CHAR ||
10225
13.5k
         token->type == token_type::INVALID_CHAR;
10226
670k
}
10227
10228
template <url_pattern_regex::regex_concept regex_provider>
10229
constexpr const token*
10230
692k
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
692k
  if (index < token_list.size()) [[likely]] {
10234
692k
    return &token_list[index];
10235
692k
  }
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
692k
}
10247
10248
template <url_pattern_regex::regex_concept regex_provider>
10249
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10250
271k
    const {
10251
  // If parser's token list[parser's token index]'s type is "open", then return
10252
  // true.
10253
271k
  return token_list[token_index].type == token_type::OPEN;
10254
271k
}
10255
10256
template <url_pattern_regex::regex_concept regex_provider>
10257
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10258
19.5k
    const {
10259
  // If parser's token list[parser's token index]'s type is "close", then return
10260
  // true.
10261
19.5k
  return token_list[token_index].type == token_type::CLOSE;
10262
19.5k
}
10263
10264
template <url_pattern_regex::regex_concept regex_provider>
10265
constexpr bool
10266
4.11k
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.11k
  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.11k
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10275
0
    return false;
10276
0
  }
10277
4.11k
  return true;
10278
4.11k
}
10279
10280
template <url_pattern_regex::regex_concept regex_provider>
10281
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10282
126k
    const {
10283
  // Return the result of running is a non-special pattern char given parser,
10284
  // parser's token index, and ":".
10285
126k
  return is_non_special_pattern_char(token_index, ':');
10286
126k
}
10287
10288
template <url_pattern_regex::regex_concept regex_provider>
10289
void constructor_string_parser<regex_provider>::change_state(State new_state,
10290
27.6k
                                                             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
27.6k
  if (state != State::INIT && state != State::AUTHORITY &&
10295
15.3k
      state != State::DONE) {
10296
15.3k
    auto value = make_component_string();
10297
    // TODO: Simplify this.
10298
15.3k
    switch (state) {
10299
4.11k
      case State::PROTOCOL: {
10300
4.11k
        result.protocol = value;
10301
4.11k
        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.11k
      case State::HOSTNAME: {
10312
4.11k
        result.hostname = value;
10313
4.11k
        break;
10314
0
      }
10315
0
      case State::PORT: {
10316
0
        result.port = value;
10317
0
        break;
10318
0
      }
10319
6.62k
      case State::PATHNAME: {
10320
6.62k
        result.pathname = value;
10321
6.62k
        break;
10322
0
      }
10323
300
      case State::SEARCH: {
10324
300
        result.search = value;
10325
300
        break;
10326
0
      }
10327
150
      case State::HASH: {
10328
150
        result.hash = value;
10329
150
        break;
10330
0
      }
10331
0
      default:
10332
0
        ada::unreachable();
10333
15.3k
    }
10334
15.3k
  }
10335
10336
  // If parser's state is not "init" and new state is not "done", then:
10337
27.6k
  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
12.7k
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10343
4.56k
         state == State::USERNAME || state == State::PASSWORD) &&
10344
8.23k
        (new_state == State::PORT || new_state == State::PATHNAME ||
10345
8.23k
         new_state == State::SEARCH || new_state == State::HASH) &&
10346
0
        !result.hostname)
10347
0
      result.hostname = "";
10348
12.7k
  }
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
27.6k
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10354
19.3k
       state == State::USERNAME || state == State::PASSWORD ||
10355
19.3k
       state == State::HOSTNAME || state == State::PORT) &&
10356
12.3k
      (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
27.6k
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10371
19.3k
       state == State::USERNAME || state == State::PASSWORD ||
10372
19.3k
       state == State::HOSTNAME || state == State::PORT ||
10373
15.2k
       state == State::PATHNAME) &&
10374
18.9k
      new_state == State::HASH && !result.search) {
10375
102
    result.search = "";
10376
102
  }
10377
10378
  // Set parser's state to new state.
10379
27.6k
  state = new_state;
10380
  // Increment parser's token index by skip.
10381
27.6k
  token_index += skip;
10382
  // Set parser's component start to parser's token index.
10383
27.6k
  component_start = token_index;
10384
  // Set parser's token increment to 0.
10385
27.6k
  token_increment = 0;
10386
27.6k
}
10387
10388
template <url_pattern_regex::regex_concept regex_provider>
10389
21.0k
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.0k
  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.0k
  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.0k
  const auto component_start_token = get_safe_token(component_start);
10399
21.0k
  ADA_ASSERT_TRUE(component_start_token);
10400
  // Let component start input index be component start token's index.
10401
21.0k
  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.0k
  return std::string(input.substr(component_start_input_index,
10405
21.0k
                                  end_index - component_start_input_index));
10406
21.0k
}
10407
10408
template <url_pattern_regex::regex_concept regex_provider>
10409
constexpr bool
10410
49.3k
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
49.3k
  return is_non_special_pattern_char(token_index, '@');
10414
49.3k
}
10415
10416
template <url_pattern_regex::regex_concept regex_provider>
10417
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
10418
98.7k
    const {
10419
  // Return the result of running is a non-special pattern char given parser,
10420
  // parser's token index, and "/".
10421
98.7k
  return is_non_special_pattern_char(token_index, '/');
10422
98.7k
}
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
49.3k
    const {
10435
  // Return the result of running is a non-special pattern char given parser,
10436
  // parser's token index, and "[".
10437
49.3k
  return is_non_special_pattern_char(token_index, '[');
10438
49.3k
}
10439
10440
template <url_pattern_regex::regex_concept regex_provider>
10441
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
10442
49.3k
    const {
10443
  // Return the result of running is a non-special pattern char given parser,
10444
  // parser's token index, and "]".
10445
49.3k
  return is_non_special_pattern_char(token_index, ']');
10446
49.3k
}
10447
10448
template <url_pattern_regex::regex_concept regex_provider>
10449
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
10450
49.3k
    const {
10451
  // Return the result of running is a non-special pattern char given parser,
10452
  // parser's token index, and ":".
10453
49.3k
  return is_non_special_pattern_char(token_index, ':');
10454
49.3k
}
10455
10456
349k
constexpr void Tokenizer::get_next_code_point() {
10457
349k
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
10458
349k
  ADA_ASSERT_TRUE(next_index < input.size());
10459
  // this assumes that we have a valid, non-truncated UTF-8 stream.
10460
349k
  code_point = 0;
10461
349k
  size_t number_bytes = 0;
10462
349k
  unsigned char first_byte = input[next_index];
10463
10464
349k
  if ((first_byte & 0x80) == 0) {
10465
    // 1-byte character (ASCII)
10466
349k
    next_index++;
10467
349k
    code_point = first_byte;
10468
349k
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
10469
349k
            uint32_t(code_point));
10470
349k
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10471
349k
            " input.size()=", input.size());
10472
349k
    return;
10473
349k
  }
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
340k
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
10504
340k
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
10505
340k
          new_index);
10506
  // Set tokenizer's next index to index.
10507
340k
  next_index = new_index;
10508
  // Run get the next code point given tokenizer.
10509
340k
  get_next_code_point();
10510
340k
}
10511
10512
inline void Tokenizer::add_token(token_type type, size_t next_position,
10513
341k
                                 size_t value_position, size_t value_length) {
10514
341k
  ada_log("Tokenizer::add_token called with type=", to_string(type),
10515
341k
          " next_position=", next_position, " value_position=", value_position);
10516
341k
  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
341k
  token_list.emplace_back(type, index,
10525
341k
                          input.substr(value_position, value_length));
10526
  // Set tokenizer's index to next position.
10527
341k
  index = next_position;
10528
341k
}
10529
10530
inline void Tokenizer::add_token_with_default_length(token_type type,
10531
                                                     size_t next_position,
10532
339k
                                                     size_t value_position) {
10533
  // Let computed length be next position - value position.
10534
339k
  auto computed_length = next_position - value_position;
10535
  // Run add a token given tokenizer, type, next position, value position, and
10536
  // computed length.
10537
339k
  add_token(type, next_position, value_position, computed_length);
10538
339k
}
10539
10540
272k
inline void Tokenizer::add_token_with_defaults(token_type type) {
10541
272k
  ada_log("Tokenizer::add_token_with_defaults called with type=",
10542
272k
          to_string(type));
10543
  // Run add a token with default length given tokenizer, type, tokenizer's next
10544
  // index, and tokenizer's index.
10545
272k
  add_token_with_default_length(type, next_index, index);
10546
272k
}
10547
10548
inline ada_warn_unused std::optional<errors>
10549
Tokenizer::process_tokenizing_error(size_t next_position,
10550
14.0k
                                    size_t value_position) {
10551
  // If tokenizer's policy is "strict", then throw a TypeError.
10552
14.0k
  if (policy == token_policy::strict) {
10553
216
    ada_log("process_tokenizing_error failed with next_position=",
10554
216
            next_position, " value_position=", value_position);
10555
216
    return errors::type_error;
10556
216
  }
10557
  // Assert: tokenizer's policy is "lenient".
10558
13.7k
  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
13.7k
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
10562
13.7k
                                value_position);
10563
13.7k
  return std::nullopt;
10564
14.0k
}
10565
10566
template <url_pattern_encoding_callback F>
10567
21.6k
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
21.6k
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
10571
  // If token is not null, then return token.
10572
21.6k
  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
21.6k
  return try_consume_token(token_type::ASTERISK);
10577
21.6k
}
10578
10579
template <url_pattern_encoding_callback F>
10580
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
10581
158k
    const token* name_token) {
10582
  // Let token be the result of running try to consume a token given parser and
10583
  // "regexp".
10584
158k
  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
158k
  if (!name_token && !token) {
10588
158k
    token = try_consume_token(token_type::ASTERISK);
10589
158k
  }
10590
  // Return token.
10591
158k
  return token;
10592
158k
}
10593
10594
template <url_pattern_encoding_callback F>
10595
805k
token* url_pattern_parser<F>::try_consume_token(token_type type) {
10596
805k
  ada_log("url_pattern_parser::try_consume_token called with type=",
10597
805k
          to_string(type));
10598
  // Assert: parser's index is less than parser's token list size.
10599
805k
  ADA_ASSERT_TRUE(index < tokens.size());
10600
  // Let next token be parser's token list[parser's index].
10601
805k
  auto& next_token = tokens[index];
10602
  // If next token's type is not type return null.
10603
805k
  if (next_token.type != type) return nullptr;
10604
  // Increase parser's index by 1.
10605
169k
  index++;
10606
  // Return next token.
10607
169k
  return &next_token;
10608
805k
}
10609
10610
template <url_pattern_encoding_callback F>
10611
2.22k
std::string url_pattern_parser<F>::consume_text() {
10612
  // Let result be the empty string.
10613
2.22k
  std::string result{};
10614
  // While true:
10615
8.65k
  while (true) {
10616
    // Let token be the result of running try to consume a token given parser
10617
    // and "char".
10618
8.65k
    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.65k
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
10622
    // If token is null, then break.
10623
8.65k
    if (!token) break;
10624
    // Append token's value to the end of result.
10625
6.42k
    result.append(token->value);
10626
6.42k
  }
10627
  // Return result.
10628
2.22k
  return result;
10629
2.22k
}
10630
10631
template <url_pattern_encoding_callback F>
10632
38.1k
bool url_pattern_parser<F>::consume_required_token(token_type type) {
10633
38.1k
  ada_log("url_pattern_parser::consume_required_token called with type=",
10634
38.1k
          to_string(type));
10635
  // Let result be the result of running try to consume a token given parser and
10636
  // type.
10637
38.1k
  return try_consume_token(type) != nullptr;
10638
38.1k
}
10639
10640
template <url_pattern_encoding_callback F>
10641
std::optional<errors>
10642
79.5k
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
79.5k
  if (pending_fixed_value.empty()) {
10645
65.8k
    ada_log("pending_fixed_value is empty");
10646
65.8k
    return std::nullopt;
10647
65.8k
  }
10648
  // Let encoded value be the result of running parser's encoding callback given
10649
  // parser's pending fixed value.
10650
13.6k
  auto encoded_value = encoding_callback(pending_fixed_value);
10651
13.6k
  if (!encoded_value) {
10652
1.34k
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
10653
1.34k
    return encoded_value.error();
10654
1.34k
  }
10655
  // Set parser's pending fixed value to the empty string.
10656
12.3k
  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.3k
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
10661
12.3k
                     std::move(*encoded_value),
10662
12.3k
                     url_pattern_part_modifier::none);
10663
12.3k
  return std::nullopt;
10664
13.6k
}
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
21.6k
    std::string_view suffix, token* modifier_token) {
10670
  // Let modifier be "none".
10671
21.6k
  auto modifier = url_pattern_part_modifier::none;
10672
  // If modifier token is not null:
10673
21.6k
  if (modifier_token) {
10674
    // If modifier token's value is "?" then set modifier to "optional".
10675
21
    if (modifier_token->value == "?") {
10676
3
      modifier = url_pattern_part_modifier::optional;
10677
18
    } else if (modifier_token->value == "*") {
10678
      // Otherwise if modifier token's value is "*" then set modifier to
10679
      // "zero-or-more".
10680
6
      modifier = url_pattern_part_modifier::zero_or_more;
10681
12
    } else if (modifier_token->value == "+") {
10682
      // Otherwise if modifier token's value is "+" then set modifier to
10683
      // "one-or-more".
10684
12
      modifier = url_pattern_part_modifier::one_or_more;
10685
12
    }
10686
21
  }
10687
  // If name token is null and regexp or wildcard token is null and modifier
10688
  // is "none":
10689
21.6k
  if (!name_token && !regexp_or_wildcard_token &&
10690
1.04k
      modifier == url_pattern_part_modifier::none) {
10691
    // Append prefix to the end of parser's pending fixed value.
10692
1.02k
    pending_fixed_value.append(prefix);
10693
1.02k
    return std::nullopt;
10694
1.02k
  }
10695
  // Run maybe add a part from the pending fixed value given parser.
10696
20.6k
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
10697
54
    return *error;
10698
54
  }
10699
  // If name token is null and regexp or wildcard token is null:
10700
20.5k
  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
20.5k
  std::string regexp_value{};
10720
  // If regexp or wildcard token is null, then set regexp value to parser's
10721
  // segment wildcard regexp.
10722
20.5k
  if (!regexp_or_wildcard_token) {
10723
0
    regexp_value = segment_wildcard_regexp;
10724
20.5k
  } 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
20.5k
    regexp_value = ".*";
10728
20.5k
  } 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
20.5k
  auto type = url_pattern_part_type::REGEXP;
10734
  // If regexp value is parser's segment wildcard regexp:
10735
20.5k
  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
20.5k
  } else if (regexp_value == ".*") {
10741
    // Otherwise if regexp value is the full wildcard regexp value:
10742
    // Set type to "full-wildcard".
10743
20.5k
    type = url_pattern_part_type::FULL_WILDCARD;
10744
    // Set regexp value to the empty string.
10745
20.5k
    regexp_value.clear();
10746
20.5k
  }
10747
  // Let name be the empty string.
10748
20.5k
  std::string name{};
10749
  // If name token is not null, then set name to name token's value.
10750
20.5k
  if (name_token) {
10751
0
    name = name_token->value;
10752
20.5k
  } 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
20.5k
    name = std::to_string(next_numeric_name);
10756
    // Increment parser's next numeric name by 1.
10757
20.5k
    next_numeric_name++;
10758
20.5k
  }
10759
  // If the result of running is a duplicate name given parser and name is
10760
  // true, then throw a TypeError.
10761
20.5k
  if (std::ranges::any_of(
10762
20.5k
          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
20.5k
  auto encoded_prefix = encoding_callback(prefix);
10768
20.5k
  if (!encoded_prefix) return encoded_prefix.error();
10769
  // Let encoded suffix be the result of running parser's encoding callback
10770
  // given suffix.
10771
20.5k
  auto encoded_suffix = encoding_callback(suffix);
10772
20.5k
  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
20.5k
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
10778
20.5k
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
10779
20.5k
  return std::nullopt;
10780
20.5k
}
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
38.6k
    F& encoding_callback) {
10786
38.6k
  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
38.6k
  auto parser = url_pattern_parser<F>(
10791
38.6k
      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
38.6k
  auto tokenize_result = tokenize(input, token_policy::strict);
10795
38.6k
  if (!tokenize_result) {
10796
216
    ada_log("parse_pattern_string tokenize failed");
10797
216
    return tl::unexpected(tokenize_result.error());
10798
216
  }
10799
38.4k
  parser.tokens = std::move(*tokenize_result);
10800
10801
  // While parser's index is less than parser's token list's size:
10802
194k
  while (parser.can_continue()) {
10803
    // Let char token be the result of running try to consume a token given
10804
    // parser and "char".
10805
157k
    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
157k
    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
157k
    auto regexp_or_wildcard_token =
10812
157k
        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
157k
    if (name_token || regexp_or_wildcard_token) {
10815
      // Let prefix be the empty string.
10816
21.0k
      std::string prefix{};
10817
      // If char token is not null then set prefix to char token's value.
10818
21.0k
      if (char_token) prefix = char_token->value;
10819
      // If prefix is not the empty string and not options's prefix code point:
10820
21.0k
      if (!prefix.empty() && prefix != options.get_prefix()) {
10821
        // Append prefix to the end of parser's pending fixed value.
10822
468
        parser.pending_fixed_value.append(prefix);
10823
        // Set prefix to the empty string.
10824
468
        prefix.clear();
10825
468
      }
10826
      // Run maybe add a part from the pending fixed value given parser.
10827
21.0k
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
10828
471
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
10829
471
        return tl::unexpected(*error);
10830
471
      }
10831
      // Let modifier token be the result of running try to consume a modifier
10832
      // token given parser.
10833
20.5k
      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
20.5k
      if (auto error =
10837
20.5k
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
10838
20.5k
                              modifier_token)) {
10839
0
        ada_log("parser.add_part failed");
10840
0
        return tl::unexpected(*error);
10841
0
      }
10842
      // Continue.
10843
20.5k
      continue;
10844
20.5k
    }
10845
10846
    // Let fixed token be char token.
10847
136k
    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
136k
    if (!fixed_token)
10851
39.6k
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
10852
    // If fixed token is not null:
10853
136k
    if (fixed_token) {
10854
      // Append fixed token's value to parser's pending fixed value.
10855
97.8k
      parser.pending_fixed_value.append(fixed_token->value);
10856
      // Continue.
10857
97.8k
      continue;
10858
97.8k
    }
10859
    // Let open token be the result of running try to consume a token given
10860
    // parser and "open".
10861
38.9k
    auto open_token = parser.try_consume_token(token_type::OPEN);
10862
    // If open token is not null:
10863
38.9k
    if (open_token) {
10864
      // Set prefix be the result of running consume text given parser.
10865
1.11k
      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
1.11k
      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
1.11k
      regexp_or_wildcard_token =
10872
1.11k
          parser.try_consume_regexp_or_wildcard_token(name_token);
10873
      // Let suffix be the result of running consume text given parser.
10874
1.11k
      auto suffix_ = parser.consume_text();
10875
      // Run consume a required token given parser and "close".
10876
1.11k
      if (!parser.consume_required_token(token_type::CLOSE)) {
10877
33
        ada_log("parser.consume_required_token failed");
10878
33
        return tl::unexpected(errors::type_error);
10879
33
      }
10880
      // Set modifier token to the result of running try to consume a modifier
10881
      // token given parser.
10882
1.08k
      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
1.08k
      if (auto error =
10886
1.08k
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
10887
1.08k
                              suffix_, modifier_token)) {
10888
54
        return tl::unexpected(*error);
10889
54
      }
10890
      // Continue.
10891
1.02k
      continue;
10892
1.08k
    }
10893
    // Run maybe add a part from the pending fixed value given parser.
10894
37.8k
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
10895
816
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
10896
816
      return tl::unexpected(*error);
10897
816
    }
10898
    // Run consume a required token given parser and "end".
10899
37.0k
    if (!parser.consume_required_token(token_type::END)) {
10900
0
      return tl::unexpected(errors::type_error);
10901
0
    }
10902
37.0k
  }
10903
37.0k
  ada_log("parser.parts size is: ", parser.parts.size());
10904
  // Return parser's part list.
10905
37.0k
  return parser.parts;
10906
38.4k
}
10907
10908
template <url_pattern_regex::regex_concept regex_provider>
10909
bool protocol_component_matches_special_scheme(
10910
8.23k
    url_pattern_component<regex_provider>& component) {
10911
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
10912
8.23k
  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.23k
    case url_pattern_component_type::EXACT_MATCH:
10917
      // Direct string comparison for exact match patterns
10918
8.23k
      return component.exact_match_value == "http" ||
10919
8.23k
             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.23k
  }
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.70k
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
10941
5.70k
  ada_log(
10942
5.70k
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
10943
5.70k
      "flag");
10944
  // Let protocol string be the result of running make a component string given
10945
  // parser.
10946
5.70k
  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.70k
  auto protocol_component = url_pattern_component<regex_provider>::compile(
10950
5.70k
      protocol_string, canonicalize_protocol,
10951
5.70k
      url_pattern_compile_component_options::DEFAULT);
10952
5.70k
  if (!protocol_component) {
10953
1.59k
    ada_log("url_pattern_component::compile failed for protocol_string ",
10954
1.59k
            protocol_string);
10955
1.59k
    return protocol_component.error();
10956
1.59k
  }
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.11k
  if (protocol_component_matches_special_scheme(*protocol_component)) {
10961
4.11k
    protocol_matches_a_special_scheme_flag = true;
10962
4.11k
  }
10963
4.11k
  return std::nullopt;
10964
5.70k
}
10965
10966
template <url_pattern_regex::regex_concept regex_provider>
10967
tl::expected<url_pattern_init, errors>
10968
8.23k
constructor_string_parser<regex_provider>::parse(std::string_view input) {
10969
8.23k
  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.23k
  auto token_list = tokenize(input, token_policy::lenient);
10973
8.23k
  if (!token_list) {
10974
0
    return tl::unexpected(token_list.error());
10975
0
  }
10976
8.23k
  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
281k
  while (parser.token_index < parser.token_list.size()) {
10980
    // Set parser's token increment to 1.
10981
281k
    parser.token_increment = 1;
10982
10983
    // If parser's token list[parser's token index]'s type is "end" then:
10984
281k
    if (parser.token_list[parser.token_index].type == token_type::END) {
10985
      // If parser's state is "init":
10986
9.12k
      if (parser.state == State::INIT) {
10987
        // Run rewind given parser.
10988
2.50k
        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.50k
        if (parser.is_hash_prefix()) {
10992
0
          parser.change_state(State::HASH, 1);
10993
2.50k
        } 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.50k
        } else {
10998
          // Run change state given parser, "pathname" and 0.
10999
2.50k
          parser.change_state(State::PATHNAME, 0);
11000
2.50k
        }
11001
        // Increment parser's token index by parser's token increment.
11002
2.50k
        parser.token_index += parser.token_increment;
11003
        // Continue.
11004
2.50k
        continue;
11005
2.50k
      }
11006
11007
6.62k
      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.62k
      parser.change_state(State::DONE, 0);
11020
      // Break.
11021
6.62k
      break;
11022
6.62k
    }
11023
11024
    // If the result of running is a group open given parser is true:
11025
271k
    if (parser.is_group_open()) {
11026
      // Increment parser's group depth by 1.
11027
3.80k
      parser.group_depth += 1;
11028
      // Increment parser's token index by parser's token increment.
11029
3.80k
      parser.token_index += parser.token_increment;
11030
3.80k
    }
11031
11032
    // If parser's group depth is greater than 0:
11033
271k
    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
19.5k
      if (parser.is_group_close()) {
11037
3.45k
        parser.group_depth -= 1;
11038
16.1k
      } else {
11039
        // Increment parser's token index by parser's token increment.
11040
16.1k
        parser.token_index += parser.token_increment;
11041
16.1k
        continue;
11042
16.1k
      }
11043
19.5k
    }
11044
11045
    // Switch on parser's state and run the associated steps:
11046
255k
    switch (parser.state) {
11047
74.7k
      case State::INIT: {
11048
        // If the result of running is a protocol suffix given parser is true:
11049
74.7k
        if (parser.is_protocol_suffix()) {
11050
          // Run rewind and set state given parser and "protocol".
11051
5.70k
          parser.rewind();
11052
5.70k
          parser.change_state(State::PROTOCOL, 0);
11053
5.70k
        }
11054
74.7k
        break;
11055
0
      }
11056
51.8k
      case State::PROTOCOL: {
11057
        // If the result of running is a protocol suffix given parser is true:
11058
51.8k
        if (parser.is_protocol_suffix()) {
11059
          // Run compute protocol matches a special scheme flag given parser.
11060
5.70k
          if (const auto error =
11061
5.70k
                  parser.compute_protocol_matches_special_scheme_flag()) {
11062
1.59k
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11063
1.59k
            return tl::unexpected(*error);
11064
1.59k
          }
11065
          // Let next state be "pathname".
11066
4.11k
          auto next_state = State::PATHNAME;
11067
          // Let skip be 1.
11068
4.11k
          auto skip = 1;
11069
          // If the result of running next is authority slashes given parser is
11070
          // true:
11071
4.11k
          if (parser.next_is_authority_slashes()) {
11072
            // Set next state to "authority".
11073
4.11k
            next_state = State::AUTHORITY;
11074
            // Set skip to 3.
11075
4.11k
            skip = 3;
11076
4.11k
          } 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.11k
          parser.change_state(next_state, skip);
11084
4.11k
        }
11085
50.2k
        break;
11086
51.8k
      }
11087
50.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
49.3k
        if (parser.is_an_identity_terminator()) {
11091
0
          parser.rewind();
11092
0
          parser.change_state(State::USERNAME, 0);
11093
49.3k
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11094
45.2k
                   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.11k
          parser.rewind();
11101
4.11k
          parser.change_state(State::HOSTNAME, 0);
11102
4.11k
        }
11103
49.3k
        break;
11104
51.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
51.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
51.8k
      }
11126
49.3k
      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
49.3k
        if (parser.is_an_ipv6_open()) {
11130
0
          parser.hostname_ipv6_bracket_depth += 1;
11131
49.3k
        } 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
49.3k
        } 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
49.3k
        } 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.11k
          parser.change_state(State::PATHNAME, 0);
11145
45.2k
        } 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
45.2k
        } 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
49.3k
        break;
11156
51.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
51.8k
      }
11173
24.6k
      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
24.6k
        if (parser.is_search_prefix()) {
11177
300
          parser.change_state(State::SEARCH, 1);
11178
24.3k
        } 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
102
          parser.change_state(State::HASH, 1);
11182
102
        }
11183
24.6k
        break;
11184
51.8k
      }
11185
4.09k
      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.09k
        if (parser.is_hash_prefix()) {
11189
48
          parser.change_state(State::HASH, 1);
11190
48
        }
11191
4.09k
        break;
11192
51.8k
      }
11193
1.63k
      case State::HASH: {
11194
        // Do nothing
11195
1.63k
        break;
11196
51.8k
      }
11197
0
      default: {
11198
        // Assert: This step is never reached.
11199
0
        unreachable();
11200
0
      }
11201
255k
    }
11202
11203
    // Increment parser's token index by parser's token increment.
11204
254k
    parser.token_index += parser.token_increment;
11205
254k
  }
11206
11207
  // If parser's result contains "hostname" and not "port", then set parser's
11208
  // result["port"] to the empty string.
11209
6.64k
  if (parser.result.hostname && !parser.result.port) {
11210
4.11k
    parser.result.port = "";
11211
4.11k
  }
11212
11213
  // Return parser's result.
11214
6.64k
  return parser.result;
11215
8.23k
}
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.1"
11232
11233
namespace ada {
11234
11235
enum {
11236
  ADA_VERSION_MAJOR = 3,
11237
  ADA_VERSION_MINOR = 4,
11238
  ADA_VERSION_REVISION = 1,
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
10.9k
                  const url_pattern_options* options) {
11265
10.9k
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
11266
10.9k
                                                        base_url, options);
11267
10.9k
}
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 */