Coverage Report

Created: 2026-04-12 06:44

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-04-10 17:33:31 -0400. Do not edit! */
2
/* begin file include/ada.h */
3
/**
4
 * @file ada.h
5
 * @brief Main header for the Ada URL parser library.
6
 *
7
 * This is the primary entry point for the Ada URL parser library. Including
8
 * this single header provides access to the complete Ada API, including:
9
 *
10
 * - URL parsing via `ada::parse()` function
11
 * - Two URL representations: `ada::url` and `ada::url_aggregator`
12
 * - URL search parameters via `ada::url_search_params`
13
 * - URL pattern matching via `ada::url_pattern` (URLPattern API)
14
 * - IDNA (Internationalized Domain Names) support
15
 *
16
 * @example
17
 * ```cpp
18
 *
19
 * // Parse a URL
20
 * auto url = ada::parse("https://example.com/path?query=1");
21
 * if (url) {
22
 *     std::cout << url->get_hostname(); // "example.com"
23
 * }
24
 * ```
25
 *
26
 * @see https://url.spec.whatwg.org/ - WHATWG URL Standard
27
 * @see https://github.com/ada-url/ada - Ada URL Parser GitHub Repository
28
 */
29
#ifndef ADA_H
30
#define ADA_H
31
32
/* begin file include/ada/ada_idna.h */
33
/* auto-generated on 2026-03-29 12:09:27 -0400. Do not edit! */
34
/* begin file include/idna.h */
35
#ifndef ADA_IDNA_H
36
#define ADA_IDNA_H
37
38
/* begin file include/ada/idna/unicode_transcoding.h */
39
#ifndef ADA_IDNA_UNICODE_TRANSCODING_H
40
#define ADA_IDNA_UNICODE_TRANSCODING_H
41
42
#include <string>
43
#include <string_view>
44
45
namespace ada::idna {
46
47
size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output);
48
49
size_t utf8_length_from_utf32(const char32_t* buf, size_t len);
50
51
size_t utf32_length_from_utf8(const char* buf, size_t len);
52
53
size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output);
54
55
}  // namespace ada::idna
56
57
#endif  // ADA_IDNA_UNICODE_TRANSCODING_H
58
/* end file include/ada/idna/unicode_transcoding.h */
59
/* begin file include/ada/idna/mapping.h */
60
#ifndef ADA_IDNA_MAPPING_H
61
#define ADA_IDNA_MAPPING_H
62
63
#include <string>
64
#include <string_view>
65
66
namespace ada::idna {
67
68
// If the input is ascii, then the mapping is just -> lower case.
69
void ascii_map(char* input, size_t length);
70
// Map the characters according to IDNA, returning the empty string on error.
71
std::u32string map(std::u32string_view input);
72
// Map into an existing buffer (cleared on entry). Returns false if any code
73
// point is disallowed. Reusing the buffer avoids repeated heap allocations
74
// when called in a loop over multiple labels.
75
bool map(std::u32string_view input, std::u32string& out);
76
77
}  // namespace ada::idna
78
79
#endif
80
/* end file include/ada/idna/mapping.h */
81
/* begin file include/ada/idna/normalization.h */
82
#ifndef ADA_IDNA_NORMALIZATION_H
83
#define ADA_IDNA_NORMALIZATION_H
84
85
#include <string>
86
#include <string_view>
87
88
namespace ada::idna {
89
90
// Normalize the characters according to IDNA (Unicode Normalization Form C).
91
void normalize(std::u32string& input);
92
93
}  // namespace ada::idna
94
#endif
95
/* end file include/ada/idna/normalization.h */
96
/* begin file include/ada/idna/punycode.h */
97
#ifndef ADA_IDNA_PUNYCODE_H
98
#define ADA_IDNA_PUNYCODE_H
99
100
#include <string>
101
#include <string_view>
102
103
namespace ada::idna {
104
105
bool punycode_to_utf32(std::string_view input, std::u32string& out);
106
bool verify_punycode(std::string_view input);
107
bool utf32_to_punycode(std::u32string_view input, std::string& out);
108
109
}  // namespace ada::idna
110
111
#endif  // ADA_IDNA_PUNYCODE_H
112
/* end file include/ada/idna/punycode.h */
113
/* begin file include/ada/idna/validity.h */
114
#ifndef ADA_IDNA_VALIDITY_H
115
#define ADA_IDNA_VALIDITY_H
116
117
#include <string>
118
#include <string_view>
119
120
namespace ada::idna {
121
122
/**
123
 * @see https://www.unicode.org/reports/tr46/#Validity_Criteria
124
 */
125
bool is_label_valid(std::u32string_view label);
126
127
}  // namespace ada::idna
128
129
#endif  // ADA_IDNA_VALIDITY_H
130
/* end file include/ada/idna/validity.h */
131
/* begin file include/ada/idna/to_ascii.h */
132
#ifndef ADA_IDNA_TO_ASCII_H
133
#define ADA_IDNA_TO_ASCII_H
134
135
#include <string>
136
#include <string_view>
137
138
namespace ada::idna {
139
140
// Converts a domain (e.g., www.google.com) possibly containing international
141
// characters to an ascii domain (with punycode). It will not do percent
142
// decoding: percent decoding should be done prior to calling this function. We
143
// do not remove tabs and spaces, they should have been removed prior to calling
144
// this function. We also do not trim control characters. We also assume that
145
// the input is not empty. We return "" on error.
146
//
147
//
148
// This function may accept or even produce invalid domains.
149
std::string to_ascii(std::string_view ut8_string);
150
151
// Returns true if the string contains a forbidden code point according to the
152
// WHATGL URL specification:
153
// https://url.spec.whatwg.org/#forbidden-domain-code-point
154
bool contains_forbidden_domain_code_point(std::string_view ascii_string);
155
156
bool constexpr is_ascii(std::u32string_view view);
157
bool constexpr is_ascii(std::string_view view);
158
159
}  // namespace ada::idna
160
161
#endif  // ADA_IDNA_TO_ASCII_H
162
/* end file include/ada/idna/to_ascii.h */
163
/* begin file include/ada/idna/to_unicode.h */
164
165
#ifndef ADA_IDNA_TO_UNICODE_H
166
#define ADA_IDNA_TO_UNICODE_H
167
168
#include <string_view>
169
170
namespace ada::idna {
171
172
std::string to_unicode(std::string_view input);
173
174
}  // namespace ada::idna
175
176
#endif  // ADA_IDNA_TO_UNICODE_H
177
/* end file include/ada/idna/to_unicode.h */
178
/* begin file include/ada/idna/identifier.h */
179
#ifndef ADA_IDNA_IDENTIFIER_H
180
#define ADA_IDNA_IDENTIFIER_H
181
182
#include <string>
183
#include <string_view>
184
185
namespace ada::idna {
186
187
// Verify if it is valid name code point given a Unicode code point and a
188
// boolean first: If first is true return the result of checking if code point
189
// is contained in the IdentifierStart set of code points. Otherwise return the
190
// result of checking if code point is contained in the IdentifierPart set of
191
// code points. Returns false if the input is empty or the code point is not
192
// valid. There is minimal Unicode error handling: the input should be valid
193
// UTF-8. https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point
194
bool valid_name_code_point(char32_t code_point, bool first);
195
196
}  // namespace ada::idna
197
198
#endif
199
/* end file include/ada/idna/identifier.h */
200
201
#endif
202
/* end file include/idna.h */
203
/* end file include/ada/ada_idna.h */
204
/* begin file include/ada/character_sets.h */
205
/**
206
 * @file character_sets.h
207
 * @brief Declaration of the character sets used by unicode functions.
208
 * @author Node.js
209
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
210
 */
211
#ifndef ADA_CHARACTER_SETS_H
212
#define ADA_CHARACTER_SETS_H
213
214
/* begin file include/ada/common_defs.h */
215
/**
216
 * @file common_defs.h
217
 * @brief Cross-platform compiler macros and common definitions.
218
 *
219
 * This header provides compiler-specific macros for optimization hints,
220
 * platform detection, SIMD support detection, and development/debug utilities.
221
 * It ensures consistent behavior across different compilers (GCC, Clang, MSVC).
222
 */
223
#ifndef ADA_COMMON_DEFS_H
224
#define ADA_COMMON_DEFS_H
225
226
// https://en.cppreference.com/w/cpp/feature_test#Library_features
227
// detect C++20 features
228
#include <version>
229
230
#ifdef _MSC_VER
231
#define ADA_VISUAL_STUDIO 1
232
/**
233
 * We want to differentiate carefully between
234
 * clang under visual studio and regular visual
235
 * studio.
236
 */
237
#ifdef __clang__
238
// clang under visual studio
239
#define ADA_CLANG_VISUAL_STUDIO 1
240
#else
241
// just regular visual studio (best guess)
242
#define ADA_REGULAR_VISUAL_STUDIO 1
243
#endif  // __clang__
244
#endif  // _MSC_VER
245
246
#if defined(__GNUC__)
247
// Marks a block with a name so that MCA analysis can see it.
248
#define ADA_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
249
#define ADA_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
250
#define ADA_DEBUG_BLOCK(name, block) \
251
  BEGIN_DEBUG_BLOCK(name);           \
252
  block;                             \
253
  END_DEBUG_BLOCK(name);
254
#else
255
#define ADA_BEGIN_DEBUG_BLOCK(name)
256
#define ADA_END_DEBUG_BLOCK(name)
257
#define ADA_DEBUG_BLOCK(name, block)
258
#endif
259
260
// Align to N-byte boundary
261
#define ADA_ROUNDUP_N(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
262
#define ADA_ROUNDDOWN_N(a, n) ((a) & ~((n) - 1))
263
264
#define ADA_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n) - 1)) == 0)
265
266
#if defined(ADA_REGULAR_VISUAL_STUDIO)
267
268
#define ada_really_inline __forceinline
269
#define ada_never_inline __declspec(noinline)
270
271
#define ada_unused
272
#define ada_warn_unused
273
274
#define ADA_PUSH_DISABLE_WARNINGS __pragma(warning(push))
275
#define ADA_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0))
276
#define ADA_DISABLE_VS_WARNING(WARNING_NUMBER) \
277
  __pragma(warning(disable : WARNING_NUMBER))
278
// Get rid of Intellisense-only warnings (Code Analysis)
279
// Though __has_include is C++17, it is supported in Visual Studio 2017 or
280
// better (_MSC_VER>=1910).
281
#ifdef __has_include
282
#if __has_include(<CppCoreCheck\Warnings.h>)
283
#include <CppCoreCheck\Warnings.h>
284
#define ADA_DISABLE_UNDESIRED_WARNINGS \
285
  ADA_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
286
#endif
287
#endif
288
289
#ifndef ADA_DISABLE_UNDESIRED_WARNINGS
290
#define ADA_DISABLE_UNDESIRED_WARNINGS
291
#endif
292
293
#define ADA_DISABLE_DEPRECATED_WARNING ADA_DISABLE_VS_WARNING(4996)
294
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING
295
#define ADA_POP_DISABLE_WARNINGS __pragma(warning(pop))
296
297
#else  // ADA_REGULAR_VISUAL_STUDIO
298
299
#define ada_really_inline inline __attribute__((always_inline))
300
#define ada_never_inline inline __attribute__((noinline))
301
302
#define ada_unused __attribute__((unused))
303
#define ada_warn_unused __attribute__((warn_unused_result))
304
305
#define ADA_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
306
// gcc doesn't seem to disable all warnings with all and extra, add warnings
307
// here as necessary
308
#define ADA_PUSH_DISABLE_ALL_WARNINGS               \
309
  ADA_PUSH_DISABLE_WARNINGS                         \
310
  ADA_DISABLE_GCC_WARNING("-Weffc++")               \
311
  ADA_DISABLE_GCC_WARNING("-Wall")                  \
312
  ADA_DISABLE_GCC_WARNING("-Wconversion")           \
313
  ADA_DISABLE_GCC_WARNING("-Wextra")                \
314
  ADA_DISABLE_GCC_WARNING("-Wattributes")           \
315
  ADA_DISABLE_GCC_WARNING("-Wimplicit-fallthrough") \
316
  ADA_DISABLE_GCC_WARNING("-Wnon-virtual-dtor")     \
317
  ADA_DISABLE_GCC_WARNING("-Wreturn-type")          \
318
  ADA_DISABLE_GCC_WARNING("-Wshadow")               \
319
  ADA_DISABLE_GCC_WARNING("-Wunused-parameter")     \
320
  ADA_DISABLE_GCC_WARNING("-Wunused-variable")      \
321
  ADA_DISABLE_GCC_WARNING("-Wsign-compare")
322
#define ADA_PRAGMA(P) _Pragma(#P)
323
#define ADA_DISABLE_GCC_WARNING(WARNING) \
324
  ADA_PRAGMA(GCC diagnostic ignored WARNING)
325
#if defined(ADA_CLANG_VISUAL_STUDIO)
326
#define ADA_DISABLE_UNDESIRED_WARNINGS \
327
  ADA_DISABLE_GCC_WARNING("-Wmicrosoft-include")
328
#else
329
#define ADA_DISABLE_UNDESIRED_WARNINGS
330
#endif
331
#define ADA_DISABLE_DEPRECATED_WARNING \
332
  ADA_DISABLE_GCC_WARNING("-Wdeprecated-declarations")
333
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING \
334
  ADA_DISABLE_GCC_WARNING("-Wstrict-overflow")
335
#define ADA_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
336
337
#endif  // MSC_VER
338
339
#if defined(ADA_VISUAL_STUDIO)
340
/**
341
 * It does not matter here whether you are using
342
 * the regular visual studio or clang under visual
343
 * studio.
344
 */
345
#if ADA_USING_LIBRARY
346
#define ADA_DLLIMPORTEXPORT __declspec(dllimport)
347
#else
348
#define ADA_DLLIMPORTEXPORT __declspec(dllexport)
349
#endif
350
#else
351
#define ADA_DLLIMPORTEXPORT
352
#endif
353
354
/// If EXPR is an error, returns it.
355
#define ADA_TRY(EXPR)   \
356
  {                     \
357
    auto _err = (EXPR); \
358
    if (_err) {         \
359
      return _err;      \
360
    }                   \
361
  }
362
363
// __has_cpp_attribute is part of C++20
364
#if !defined(__has_cpp_attribute)
365
#define __has_cpp_attribute(x) 0
366
#endif
367
368
#if __has_cpp_attribute(gnu::noinline)
369
#define ADA_ATTRIBUTE_NOINLINE [[gnu::noinline]]
370
#else
371
#define ADA_ATTRIBUTE_NOINLINE
372
#endif
373
374
namespace ada {
375
0
[[noreturn]] inline void unreachable() {
376
0
#ifdef __GNUC__
377
0
  __builtin_unreachable();
378
#elif defined(_MSC_VER)
379
  __assume(false);
380
#else
381
#endif
382
0
}
383
}  // namespace ada
384
385
// Unless the programmer has already set ADA_DEVELOPMENT_CHECKS,
386
// we want to set it under debug builds. We detect a debug build
387
// under Visual Studio when the _DEBUG macro is set. Under the other
388
// compilers, we use the fact that they define __OPTIMIZE__ whenever
389
// they allow optimizations.
390
// It is possible that this could miss some cases where ADA_DEVELOPMENT_CHECKS
391
// is helpful, but the programmer can set the macro ADA_DEVELOPMENT_CHECKS.
392
// It could also wrongly set ADA_DEVELOPMENT_CHECKS (e.g., if the programmer
393
// sets _DEBUG in a release build under Visual Studio, or if some compiler fails
394
// to set the __OPTIMIZE__ macro).
395
#if !defined(ADA_DEVELOPMENT_CHECKS) && !defined(NDEBUG)
396
#ifdef _MSC_VER
397
// Visual Studio seems to set _DEBUG for debug builds.
398
#ifdef _DEBUG
399
#define ADA_DEVELOPMENT_CHECKS 1
400
#endif  // _DEBUG
401
#else   // _MSC_VER
402
// All other compilers appear to set __OPTIMIZE__ to a positive integer
403
// when the compiler is optimizing.
404
#ifndef __OPTIMIZE__
405
#define ADA_DEVELOPMENT_CHECKS 1
406
#endif  // __OPTIMIZE__
407
#endif  // _MSC_VER
408
#endif  // ADA_DEVELOPMENT_CHECKS
409
410
#define ADA_STR(x) #x
411
412
#if ADA_DEVELOPMENT_CHECKS
413
#define ADA_REQUIRE(EXPR) \
414
  {                       \
415
    if (!(EXPR) { abort(); }) }
416
417
#define ADA_FAIL(MESSAGE)                            \
418
  do {                                               \
419
    std::cerr << "FAIL: " << (MESSAGE) << std::endl; \
420
    abort();                                         \
421
  } while (0);
422
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)                                    \
423
  do {                                                                         \
424
    if (LHS != RHS) {                                                          \
425
      std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \
426
      ADA_FAIL(MESSAGE);                                                       \
427
    }                                                                          \
428
  } while (0);
429
#define ADA_ASSERT_TRUE(COND)                                               \
430
  do {                                                                      \
431
    if (!(COND)) {                                                          \
432
      std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \
433
                << std::endl;                                               \
434
      ADA_FAIL(ADA_STR(COND));                                              \
435
    }                                                                       \
436
  } while (0);
437
#else
438
#define ADA_FAIL(MESSAGE)
439
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)
440
#define ADA_ASSERT_TRUE(COND)
441
#endif
442
443
#ifdef ADA_VISUAL_STUDIO
444
#define ADA_ASSUME(COND) __assume(COND)
445
#else
446
#define ADA_ASSUME(COND)       \
447
  do {                         \
448
    if (!(COND)) {             \
449
      __builtin_unreachable(); \
450
    }                          \
451
  } while (0)
452
#endif
453
454
#if defined(__SSSE3__)
455
#define ADA_SSSE3 1
456
#endif
457
458
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
459
    (defined(_M_AMD64) || defined(_M_X64) ||                         \
460
     (defined(_M_IX86_FP) && _M_IX86_FP == 2))
461
#define ADA_SSE2 1
462
#endif
463
464
#if defined(__aarch64__) || defined(_M_ARM64)
465
#define ADA_NEON 1
466
#endif
467
468
#if defined(__loongarch_sx)
469
#define ADA_LSX 1
470
#endif
471
472
#if defined(__riscv_v) && __riscv_v_intrinsic >= 11000
473
// Support RVV intrinsics v0.11 and above
474
#define ADA_RVV 1
475
#endif
476
477
#ifndef __has_cpp_attribute
478
#define ada_lifetime_bound
479
#elif __has_cpp_attribute(msvc::lifetimebound)
480
#define ada_lifetime_bound [[msvc::lifetimebound]]
481
#elif __has_cpp_attribute(clang::lifetimebound)
482
#define ada_lifetime_bound [[clang::lifetimebound]]
483
#elif __has_cpp_attribute(lifetimebound)
484
#define ada_lifetime_bound [[lifetimebound]]
485
#else
486
#define ada_lifetime_bound
487
#endif
488
489
#ifdef __cpp_lib_format
490
#if __cpp_lib_format >= 202110L
491
#include <format>
492
#define ADA_HAS_FORMAT 1
493
#endif
494
#endif
495
496
#ifndef ADA_INCLUDE_URL_PATTERN
497
#define ADA_INCLUDE_URL_PATTERN 1
498
#endif  // ADA_INCLUDE_URL_PATTERN
499
500
#endif  // ADA_COMMON_DEFS_H
501
/* end file include/ada/common_defs.h */
502
#include <cstdint>
503
504
/**
505
 * These functions are not part of our public API and may
506
 * change at any time.
507
 * @private
508
 * @namespace ada::character_sets
509
 * @brief Includes the definitions for unicode character sets.
510
 */
511
namespace ada::character_sets {
512
ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i);
513
}  // namespace ada::character_sets
514
515
#endif  // ADA_CHARACTER_SETS_H
516
/* end file include/ada/character_sets.h */
517
/* begin file include/ada/character_sets-inl.h */
518
/**
519
 * @file character_sets-inl.h
520
 * @brief Definitions of the character sets used by unicode functions.
521
 * @author Node.js
522
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
523
 */
524
#ifndef ADA_CHARACTER_SETS_INL_H
525
#define ADA_CHARACTER_SETS_INL_H
526
527
528
/**
529
 * These functions are not part of our public API and may
530
 * change at any time.
531
 * @private
532
 */
533
namespace ada::character_sets {
534
535
constexpr char hex[1024] =
536
    "%00\0%01\0%02\0%03\0%04\0%05\0%06\0%07\0"
537
    "%08\0%09\0%0A\0%0B\0%0C\0%0D\0%0E\0%0F\0"
538
    "%10\0%11\0%12\0%13\0%14\0%15\0%16\0%17\0"
539
    "%18\0%19\0%1A\0%1B\0%1C\0%1D\0%1E\0%1F\0"
540
    "%20\0%21\0%22\0%23\0%24\0%25\0%26\0%27\0"
541
    "%28\0%29\0%2A\0%2B\0%2C\0%2D\0%2E\0%2F\0"
542
    "%30\0%31\0%32\0%33\0%34\0%35\0%36\0%37\0"
543
    "%38\0%39\0%3A\0%3B\0%3C\0%3D\0%3E\0%3F\0"
544
    "%40\0%41\0%42\0%43\0%44\0%45\0%46\0%47\0"
545
    "%48\0%49\0%4A\0%4B\0%4C\0%4D\0%4E\0%4F\0"
546
    "%50\0%51\0%52\0%53\0%54\0%55\0%56\0%57\0"
547
    "%58\0%59\0%5A\0%5B\0%5C\0%5D\0%5E\0%5F\0"
548
    "%60\0%61\0%62\0%63\0%64\0%65\0%66\0%67\0"
549
    "%68\0%69\0%6A\0%6B\0%6C\0%6D\0%6E\0%6F\0"
550
    "%70\0%71\0%72\0%73\0%74\0%75\0%76\0%77\0"
551
    "%78\0%79\0%7A\0%7B\0%7C\0%7D\0%7E\0%7F\0"
552
    "%80\0%81\0%82\0%83\0%84\0%85\0%86\0%87\0"
553
    "%88\0%89\0%8A\0%8B\0%8C\0%8D\0%8E\0%8F\0"
554
    "%90\0%91\0%92\0%93\0%94\0%95\0%96\0%97\0"
555
    "%98\0%99\0%9A\0%9B\0%9C\0%9D\0%9E\0%9F\0"
556
    "%A0\0%A1\0%A2\0%A3\0%A4\0%A5\0%A6\0%A7\0"
557
    "%A8\0%A9\0%AA\0%AB\0%AC\0%AD\0%AE\0%AF\0"
558
    "%B0\0%B1\0%B2\0%B3\0%B4\0%B5\0%B6\0%B7\0"
559
    "%B8\0%B9\0%BA\0%BB\0%BC\0%BD\0%BE\0%BF\0"
560
    "%C0\0%C1\0%C2\0%C3\0%C4\0%C5\0%C6\0%C7\0"
561
    "%C8\0%C9\0%CA\0%CB\0%CC\0%CD\0%CE\0%CF\0"
562
    "%D0\0%D1\0%D2\0%D3\0%D4\0%D5\0%D6\0%D7\0"
563
    "%D8\0%D9\0%DA\0%DB\0%DC\0%DD\0%DE\0%DF\0"
564
    "%E0\0%E1\0%E2\0%E3\0%E4\0%E5\0%E6\0%E7\0"
565
    "%E8\0%E9\0%EA\0%EB\0%EC\0%ED\0%EE\0%EF\0"
566
    "%F0\0%F1\0%F2\0%F3\0%F4\0%F5\0%F6\0%F7\0"
567
    "%F8\0%F9\0%FA\0%FB\0%FC\0%FD\0%FE\0%FF";
568
569
constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32] = {
570
    // 00     01     02     03     04     05     06     07
571
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
572
    // 08     09     0A     0B     0C     0D     0E     0F
573
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
574
    // 10     11     12     13     14     15     16     17
575
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
576
    // 18     19     1A     1B     1C     1D     1E     1F
577
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
578
    // 20     21     22     23     24     25     26     27
579
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
580
    // 28     29     2A     2B     2C     2D     2E     2F
581
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
582
    // 30     31     32     33     34     35     36     37
583
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
584
    // 38     39     3A     3B     3C     3D     3E     3F
585
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
586
    // 40     41     42     43     44     45     46     47
587
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
588
    // 48     49     4A     4B     4C     4D     4E     4F
589
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
590
    // 50     51     52     53     54     55     56     57
591
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
592
    // 58     59     5A     5B     5C     5D     5E     5F
593
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
594
    // 60     61     62     63     64     65     66     67
595
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
596
    // 68     69     6A     6B     6C     6D     6E     6F
597
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
598
    // 70     71     72     73     74     75     76     77
599
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
600
    // 78     79     7A     7B     7C     7D     7E     7F
601
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
602
    // 80     81     82     83     84     85     86     87
603
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
604
    // 88     89     8A     8B     8C     8D     8E     8F
605
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
606
    // 90     91     92     93     94     95     96     97
607
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
608
    // 98     99     9A     9B     9C     9D     9E     9F
609
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
610
    // A0     A1     A2     A3     A4     A5     A6     A7
611
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
612
    // A8     A9     AA     AB     AC     AD     AE     AF
613
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
614
    // B0     B1     B2     B3     B4     B5     B6     B7
615
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
616
    // B8     B9     BA     BB     BC     BD     BE     BF
617
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
618
    // C0     C1     C2     C3     C4     C5     C6     C7
619
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
620
    // C8     C9     CA     CB     CC     CD     CE     CF
621
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
622
    // D0     D1     D2     D3     D4     D5     D6     D7
623
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
624
    // D8     D9     DA     DB     DC     DD     DE     DF
625
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
626
    // E0     E1     E2     E3     E4     E5     E6     E7
627
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
628
    // E8     E9     EA     EB     EC     ED     EE     EF
629
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
630
    // F0     F1     F2     F3     F4     F5     F6     F7
631
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
632
    // F8     F9     FA     FB     FC     FD     FE     FF
633
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
634
635
constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32] = {
636
    // 00     01     02     03     04     05     06     07
637
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
638
    // 08     09     0A     0B     0C     0D     0E     0F
639
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
640
    // 10     11     12     13     14     15     16     17
641
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
642
    // 18     19     1A     1B     1C     1D     1E     1F
643
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
644
    // 20     21     22     23     24     25     26     27
645
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x80,
646
    // 28     29     2A     2B     2C     2D     2E     2F
647
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
648
    // 30     31     32     33     34     35     36     37
649
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
650
    // 38     39     3A     3B     3C     3D     3E     3F
651
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
652
    // 40     41     42     43     44     45     46     47
653
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
654
    // 48     49     4A     4B     4C     4D     4E     4F
655
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
656
    // 50     51     52     53     54     55     56     57
657
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
658
    // 58     59     5A     5B     5C     5D     5E     5F
659
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
660
    // 60     61     62     63     64     65     66     67
661
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
662
    // 68     69     6A     6B     6C     6D     6E     6F
663
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
664
    // 70     71     72     73     74     75     76     77
665
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
666
    // 78     79     7A     7B     7C     7D     7E     7F
667
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
668
    // 80     81     82     83     84     85     86     87
669
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
670
    // 88     89     8A     8B     8C     8D     8E     8F
671
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
672
    // 90     91     92     93     94     95     96     97
673
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
674
    // 98     99     9A     9B     9C     9D     9E     9F
675
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
676
    // A0     A1     A2     A3     A4     A5     A6     A7
677
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
678
    // A8     A9     AA     AB     AC     AD     AE     AF
679
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
680
    // B0     B1     B2     B3     B4     B5     B6     B7
681
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
682
    // B8     B9     BA     BB     BC     BD     BE     BF
683
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
684
    // C0     C1     C2     C3     C4     C5     C6     C7
685
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
686
    // C8     C9     CA     CB     CC     CD     CE     CF
687
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
688
    // D0     D1     D2     D3     D4     D5     D6     D7
689
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
690
    // D8     D9     DA     DB     DC     DD     DE     DF
691
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
692
    // E0     E1     E2     E3     E4     E5     E6     E7
693
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
694
    // E8     E9     EA     EB     EC     ED     EE     EF
695
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
696
    // F0     F1     F2     F3     F4     F5     F6     F7
697
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
698
    // F8     F9     FA     FB     FC     FD     FE     FF
699
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
700
701
constexpr uint8_t QUERY_PERCENT_ENCODE[32] = {
702
    // 00     01     02     03     04     05     06     07
703
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
704
    // 08     09     0A     0B     0C     0D     0E     0F
705
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
706
    // 10     11     12     13     14     15     16     17
707
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
708
    // 18     19     1A     1B     1C     1D     1E     1F
709
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
710
    // 20     21     22     23     24     25     26     27
711
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
712
    // 28     29     2A     2B     2C     2D     2E     2F
713
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
714
    // 30     31     32     33     34     35     36     37
715
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
716
    // 38     39     3A     3B     3C     3D     3E     3F
717
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
718
    // 40     41     42     43     44     45     46     47
719
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
720
    // 48     49     4A     4B     4C     4D     4E     4F
721
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
722
    // 50     51     52     53     54     55     56     57
723
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
724
    // 58     59     5A     5B     5C     5D     5E     5F
725
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
726
    // 60     61     62     63     64     65     66     67
727
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
728
    // 68     69     6A     6B     6C     6D     6E     6F
729
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
730
    // 70     71     72     73     74     75     76     77
731
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
732
    // 78     79     7A     7B     7C     7D     7E     7F
733
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
734
    // 80     81     82     83     84     85     86     87
735
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
736
    // 88     89     8A     8B     8C     8D     8E     8F
737
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
738
    // 90     91     92     93     94     95     96     97
739
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
740
    // 98     99     9A     9B     9C     9D     9E     9F
741
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
742
    // A0     A1     A2     A3     A4     A5     A6     A7
743
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
744
    // A8     A9     AA     AB     AC     AD     AE     AF
745
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
746
    // B0     B1     B2     B3     B4     B5     B6     B7
747
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
748
    // B8     B9     BA     BB     BC     BD     BE     BF
749
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
750
    // C0     C1     C2     C3     C4     C5     C6     C7
751
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
752
    // C8     C9     CA     CB     CC     CD     CE     CF
753
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
754
    // D0     D1     D2     D3     D4     D5     D6     D7
755
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
756
    // D8     D9     DA     DB     DC     DD     DE     DF
757
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
758
    // E0     E1     E2     E3     E4     E5     E6     E7
759
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
760
    // E8     E9     EA     EB     EC     ED     EE     EF
761
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
762
    // F0     F1     F2     F3     F4     F5     F6     F7
763
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
764
    // F8     F9     FA     FB     FC     FD     FE     FF
765
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
766
767
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32] = {
768
    // 00     01     02     03     04     05     06     07
769
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
770
    // 08     09     0A     0B     0C     0D     0E     0F
771
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
772
    // 10     11     12     13     14     15     16     17
773
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
774
    // 18     19     1A     1B     1C     1D     1E     1F
775
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
776
    // 20     21     22     23     24     25     26     27
777
    0x01 | 0x00 | 0x04 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
778
    // 28     29     2A     2B     2C     2D     2E     2F
779
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
780
    // 30     31     32     33     34     35     36     37
781
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
782
    // 38     39     3A     3B     3C     3D     3E     3F
783
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
784
    // 40     41     42     43     44     45     46     47
785
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
786
    // 48     49     4A     4B     4C     4D     4E     4F
787
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
788
    // 50     51     52     53     54     55     56     57
789
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
790
    // 58     59     5A     5B     5C     5D     5E     5F
791
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
792
    // 60     61     62     63     64     65     66     67
793
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
794
    // 68     69     6A     6B     6C     6D     6E     6F
795
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
796
    // 70     71     72     73     74     75     76     77
797
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
798
    // 78     79     7A     7B     7C     7D     7E     7F
799
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
800
    // 80     81     82     83     84     85     86     87
801
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
802
    // 88     89     8A     8B     8C     8D     8E     8F
803
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
804
    // 90     91     92     93     94     95     96     97
805
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
806
    // 98     99     9A     9B     9C     9D     9E     9F
807
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
808
    // A0     A1     A2     A3     A4     A5     A6     A7
809
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
810
    // A8     A9     AA     AB     AC     AD     AE     AF
811
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
812
    // B0     B1     B2     B3     B4     B5     B6     B7
813
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
814
    // B8     B9     BA     BB     BC     BD     BE     BF
815
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
816
    // C0     C1     C2     C3     C4     C5     C6     C7
817
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
818
    // C8     C9     CA     CB     CC     CD     CE     CF
819
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
820
    // D0     D1     D2     D3     D4     D5     D6     D7
821
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
822
    // D8     D9     DA     DB     DC     DD     DE     DF
823
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
824
    // E0     E1     E2     E3     E4     E5     E6     E7
825
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
826
    // E8     E9     EA     EB     EC     ED     EE     EF
827
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
828
    // F0     F1     F2     F3     F4     F5     F6     F7
829
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
830
    // F8     F9     FA     FB     FC     FD     FE     FF
831
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
832
833
constexpr uint8_t USERINFO_PERCENT_ENCODE[32] = {
834
    // 00     01     02     03     04     05     06     07
835
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
836
    // 08     09     0A     0B     0C     0D     0E     0F
837
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
838
    // 10     11     12     13     14     15     16     17
839
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
840
    // 18     19     1A     1B     1C     1D     1E     1F
841
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
842
    // 20     21     22     23     24     25     26     27
843
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
844
    // 28     29     2A     2B     2C     2D     2E     2F
845
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
846
    // 30     31     32     33     34     35     36     37
847
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
848
    // 38     39     3A     3B     3C     3D     3E     3F
849
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
850
    // 40     41     42     43     44     45     46     47
851
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
852
    // 48     49     4A     4B     4C     4D     4E     4F
853
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
854
    // 50     51     52     53     54     55     56     57
855
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
856
    // 58     59     5A     5B     5C     5D     5E     5F
857
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
858
    // 60     61     62     63     64     65     66     67
859
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
860
    // 68     69     6A     6B     6C     6D     6E     6F
861
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
862
    // 70     71     72     73     74     75     76     77
863
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
864
    // 78     79     7A     7B     7C     7D     7E     7F
865
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80,
866
    // 80     81     82     83     84     85     86     87
867
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
868
    // 88     89     8A     8B     8C     8D     8E     8F
869
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
870
    // 90     91     92     93     94     95     96     97
871
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
872
    // 98     99     9A     9B     9C     9D     9E     9F
873
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
874
    // A0     A1     A2     A3     A4     A5     A6     A7
875
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
876
    // A8     A9     AA     AB     AC     AD     AE     AF
877
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
878
    // B0     B1     B2     B3     B4     B5     B6     B7
879
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
880
    // B8     B9     BA     BB     BC     BD     BE     BF
881
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
882
    // C0     C1     C2     C3     C4     C5     C6     C7
883
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
884
    // C8     C9     CA     CB     CC     CD     CE     CF
885
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
886
    // D0     D1     D2     D3     D4     D5     D6     D7
887
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
888
    // D8     D9     DA     DB     DC     DD     DE     DF
889
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
890
    // E0     E1     E2     E3     E4     E5     E6     E7
891
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
892
    // E8     E9     EA     EB     EC     ED     EE     EF
893
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
894
    // F0     F1     F2     F3     F4     F5     F6     F7
895
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
896
    // F8     F9     FA     FB     FC     FD     FE     FF
897
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
898
899
constexpr uint8_t PATH_PERCENT_ENCODE[32] = {
900
    // 00     01     02     03     04     05     06     07
901
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
902
    // 08     09     0A     0B     0C     0D     0E     0F
903
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
904
    // 10     11     12     13     14     15     16     17
905
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
906
    // 18     19     1A     1B     1C     1D     1E     1F
907
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
908
    // 20     21     22     23     24     25     26     27
909
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
910
    // 28     29     2A     2B     2C     2D     2E     2F
911
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
912
    // 30     31     32     33     34     35     36     37
913
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
914
    // 38     39     3A     3B     3C     3D     3E     3F
915
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
916
    // 40     41     42     43     44     45     46     47
917
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
918
    // 48     49     4A     4B     4C     4D     4E     4F
919
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
920
    // 50     51     52     53     54     55     56     57
921
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
922
    // 58     59     5A     5B     5C     5D     5E     5F
923
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x40 | 0x00,
924
    // 60     61     62     63     64     65     66     67
925
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
926
    // 68     69     6A     6B     6C     6D     6E     6F
927
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
928
    // 70     71     72     73     74     75     76     77
929
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
930
    // 78     79     7A     7B     7C     7D     7E     7F
931
    0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80,
932
    // 80     81     82     83     84     85     86     87
933
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
934
    // 88     89     8A     8B     8C     8D     8E     8F
935
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
936
    // 90     91     92     93     94     95     96     97
937
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
938
    // 98     99     9A     9B     9C     9D     9E     9F
939
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
940
    // A0     A1     A2     A3     A4     A5     A6     A7
941
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
942
    // A8     A9     AA     AB     AC     AD     AE     AF
943
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
944
    // B0     B1     B2     B3     B4     B5     B6     B7
945
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
946
    // B8     B9     BA     BB     BC     BD     BE     BF
947
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
948
    // C0     C1     C2     C3     C4     C5     C6     C7
949
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
950
    // C8     C9     CA     CB     CC     CD     CE     CF
951
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
952
    // D0     D1     D2     D3     D4     D5     D6     D7
953
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
954
    // D8     D9     DA     DB     DC     DD     DE     DF
955
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
956
    // E0     E1     E2     E3     E4     E5     E6     E7
957
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
958
    // E8     E9     EA     EB     EC     ED     EE     EF
959
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
960
    // F0     F1     F2     F3     F4     F5     F6     F7
961
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
962
    // F8     F9     FA     FB     FC     FD     FE     FF
963
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
964
965
constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = {
966
    // 00     01     02     03     04     05     06     07
967
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
968
    // 08     09     0A     0B     0C     0D     0E     0F
969
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
970
    // 10     11     12     13     14     15     16     17
971
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
972
    // 18     19     1A     1B     1C     1D     1E     1F
973
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
974
    // 20     21     22     23     24     25     26     27
975
    0x00 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
976
    // 28     29     2A     2B     2C     2D     2E     2F
977
    0x01 | 0x02 | 0x00 | 0x08 | 0x10 | 0x00 | 0x00 | 0x80,
978
    // 30     31     32     33     34     35     36     37
979
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
980
    // 38     39     3A     3B     3C     3D     3E     3F
981
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
982
    // 40     41     42     43     44     45     46     47
983
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
984
    // 48     49     4A     4B     4C     4D     4E     4F
985
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
986
    // 50     51     52     53     54     55     56     57
987
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
988
    // 58     59     5A     5B     5C     5D     5E     5F
989
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
990
    // 60     61     62     63     64     65     66     67
991
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
992
    // 68     69     6A     6B     6C     6D     6E     6F
993
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
994
    // 70     71     72     73     74     75     76     77
995
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
996
    // 78     79     7A     7B     7C     7D     7E     7F
997
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
998
    // 80     81     82     83     84     85     86     87
999
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1000
    // 88     89     8A     8B     8C     8D     8E     8F
1001
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1002
    // 90     91     92     93     94     95     96     97
1003
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1004
    // 98     99     9A     9B     9C     9D     9E     9F
1005
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1006
    // A0     A1     A2     A3     A4     A5     A6     A7
1007
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1008
    // A8     A9     AA     AB     AC     AD     AE     AF
1009
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1010
    // B0     B1     B2     B3     B4     B5     B6     B7
1011
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1012
    // B8     B9     BA     BB     BC     BD     BE     BF
1013
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1014
    // C0     C1     C2     C3     C4     C5     C6     C7
1015
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1016
    // C8     C9     CA     CB     CC     CD     CE     CF
1017
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1018
    // D0     D1     D2     D3     D4     D5     D6     D7
1019
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1020
    // D8     D9     DA     DB     DC     DD     DE     DF
1021
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1022
    // E0     E1     E2     E3     E4     E5     E6     E7
1023
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1024
    // E8     E9     EA     EB     EC     ED     EE     EF
1025
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1026
    // F0     F1     F2     F3     F4     F5     F6     F7
1027
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1028
    // F8     F9     FA     FB     FC     FD     FE     FF
1029
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1030
1031
1.05M
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1032
1.05M
  return !!(a[i >> 3] & (1 << (i & 7)));
1033
1.05M
}
1034
1035
}  // namespace ada::character_sets
1036
1037
#endif  // ADA_CHARACTER_SETS_INL_H
1038
/* end file include/ada/character_sets-inl.h */
1039
/* begin file include/ada/checkers-inl.h */
1040
/**
1041
 * @file checkers-inl.h
1042
 * @brief Definitions for URL specific checkers used within Ada.
1043
 */
1044
#ifndef ADA_CHECKERS_INL_H
1045
#define ADA_CHECKERS_INL_H
1046
1047
#include <bit>
1048
#include <string_view>
1049
/* begin file include/ada/checkers.h */
1050
/**
1051
 * @file checkers.h
1052
 * @brief Declarations for URL specific checkers used within Ada.
1053
 */
1054
#ifndef ADA_CHECKERS_H
1055
#define ADA_CHECKERS_H
1056
1057
1058
#include <cstring>
1059
#include <string_view>
1060
1061
/**
1062
 * These functions are not part of our public API and may
1063
 * change at any time.
1064
 * @private
1065
 * @namespace ada::checkers
1066
 * @brief Includes the definitions for validation functions
1067
 */
1068
namespace ada::checkers {
1069
1070
/**
1071
 * @private
1072
 * Assuming that x is an ASCII letter, this function returns the lower case
1073
 * equivalent.
1074
 * @details More likely to be inlined by the compiler and constexpr.
1075
 */
1076
constexpr char to_lower(char x) noexcept;
1077
1078
/**
1079
 * @private
1080
 * Returns true if the character is an ASCII letter. Equivalent to std::isalpha
1081
 * but more likely to be inlined by the compiler.
1082
 *
1083
 * @attention std::isalpha is not constexpr generally.
1084
 */
1085
constexpr bool is_alpha(char x) noexcept;
1086
1087
/**
1088
 * @private
1089
 * Check whether a string starts with 0x or 0X. The function is only
1090
 * safe if input.size() >=2.
1091
 *
1092
 * @see has_hex_prefix
1093
 */
1094
constexpr bool has_hex_prefix_unsafe(std::string_view input);
1095
/**
1096
 * @private
1097
 * Check whether a string starts with 0x or 0X.
1098
 */
1099
constexpr bool has_hex_prefix(std::string_view input);
1100
1101
/**
1102
 * @private
1103
 * Check whether x is an ASCII digit. More likely to be inlined than
1104
 * std::isdigit.
1105
 */
1106
constexpr bool is_digit(char x) noexcept;
1107
1108
/**
1109
 * @private
1110
 * @details A string starts with a Windows drive letter if all of the following
1111
 * are true:
1112
 *
1113
 *   - its length is greater than or equal to 2
1114
 *   - its first two code points are a Windows drive letter
1115
 *   - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F
1116
 * (?), or U+0023 (#).
1117
 *
1118
 * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
1119
 */
1120
inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept;
1121
1122
/**
1123
 * @private
1124
 * @details A normalized Windows drive letter is a Windows drive letter of which
1125
 * the second code point is U+003A (:).
1126
 */
1127
inline constexpr bool is_normalized_windows_drive_letter(
1128
    std::string_view input) noexcept;
1129
1130
/**
1131
 * @private
1132
 * Returns true if an input is an ipv4 address. It is assumed that the string
1133
 * does not contain uppercase ASCII characters (the input should have been
1134
 * lowered cased before calling this function) and is not empty.
1135
 */
1136
ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept;
1137
1138
/**
1139
 * @private
1140
 * Returns a bitset. If the first bit is set, then at least one character needs
1141
 * percent encoding. If the second bit is set, a \\ is found. If the third bit
1142
 * is set then we have a dot. If the fourth bit is set, then we have a percent
1143
 * character.
1144
 */
1145
ada_really_inline constexpr uint8_t path_signature(
1146
    std::string_view input) noexcept;
1147
1148
/**
1149
 * @private
1150
 * Returns true if the length of the domain name and its labels are according to
1151
 * the specifications. The length of the domain must be 255 octets (253
1152
 * characters not including the last 2 which are the empty label reserved at the
1153
 * end). When the empty label is included (a dot at the end), the domain name
1154
 * can have 254 characters. The length of a label must be at least 1 and at most
1155
 * 63 characters.
1156
 * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034
1157
 * @see https://www.unicode.org/reports/tr46/#ToASCII
1158
 */
1159
ada_really_inline constexpr bool verify_dns_length(
1160
    std::string_view input) noexcept;
1161
1162
/**
1163
 * @private
1164
 * Fast-path parser for pure decimal IPv4 addresses (e.g., "192.168.1.1").
1165
 * Returns the packed 32-bit IPv4 address on success, or a value > 0xFFFFFFFF
1166
 * to indicate failure (caller should fall back to general parser).
1167
 * This is optimized for the common case where the input is a well-formed
1168
 * decimal IPv4 address with exactly 4 octets.
1169
 */
1170
ada_really_inline constexpr uint64_t try_parse_ipv4_fast(
1171
    std::string_view input) noexcept;
1172
1173
/**
1174
 * Sentinel value indicating try_parse_ipv4_fast() did not succeed.
1175
 * Any value > 0xFFFFFFFF indicates the fast path should not be used.
1176
 */
1177
constexpr uint64_t ipv4_fast_fail = uint64_t(1) << 32;
1178
1179
}  // namespace ada::checkers
1180
1181
#endif  // ADA_CHECKERS_H
1182
/* end file include/ada/checkers.h */
1183
1184
namespace ada::checkers {
1185
1186
715
constexpr bool has_hex_prefix_unsafe(std::string_view input) {
1187
  // This is actually efficient code, see has_hex_prefix for the assembly.
1188
715
  constexpr bool is_little_endian = std::endian::native == std::endian::little;
1189
715
  constexpr uint16_t word0x = 0x7830;
1190
715
  uint16_t two_first_bytes =
1191
715
      static_cast<uint16_t>(input[0]) |
1192
715
      static_cast<uint16_t>((static_cast<uint16_t>(input[1]) << 8));
1193
715
  if constexpr (is_little_endian) {
1194
715
    two_first_bytes |= 0x2000;
1195
  } else {
1196
    two_first_bytes |= 0x020;
1197
  }
1198
715
  return two_first_bytes == word0x;
1199
715
}
1200
1201
737
constexpr bool has_hex_prefix(std::string_view input) {
1202
737
  return input.size() >= 2 && has_hex_prefix_unsafe(input);
1203
737
}
1204
1205
13.7k
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1206
1207
13.4k
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1208
1209
7.75k
constexpr bool is_alpha(char x) noexcept {
1210
7.75k
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1211
7.75k
}
1212
1213
2.45k
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1214
2.45k
  return input.size() >= 2 &&
1215
1.80k
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1216
8
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1217
3
                                  input[2] == '?' || input[2] == '#'));
1218
2.45k
}
1219
1220
constexpr bool is_normalized_windows_drive_letter(
1221
2.45k
    std::string_view input) noexcept {
1222
2.45k
  return input.size() >= 2 && (is_alpha(input[0]) && (input[1] == ':'));
1223
2.45k
}
1224
1225
ada_really_inline constexpr uint64_t try_parse_ipv4_fast(
1226
4.08k
    std::string_view input) noexcept {
1227
4.08k
  const char* p = input.data();
1228
4.08k
  const char* const pend = p + input.size();
1229
1230
4.08k
  uint32_t ipv4 = 0;
1231
1232
5.03k
  for (int i = 0; i < 4; ++i) {
1233
4.84k
    if (p == pend) {
1234
311
      return ipv4_fast_fail;
1235
311
    }
1236
1237
4.53k
    uint32_t val;
1238
4.53k
    char c = *p;
1239
4.53k
    if (c >= '0' && c <= '9') {
1240
1.69k
      val = c - '0';
1241
1.69k
      p++;
1242
2.83k
    } else {
1243
2.83k
      return ipv4_fast_fail;
1244
2.83k
    }
1245
1246
1.69k
    if (p < pend) {
1247
1.62k
      c = *p;
1248
1.62k
      if (c >= '0' && c <= '9') {
1249
945
        if (val == 0) return ipv4_fast_fail;
1250
812
        val = val * 10 + (c - '0');
1251
812
        p++;
1252
812
        if (p < pend) {
1253
750
          c = *p;
1254
750
          if (c >= '0' && c <= '9') {
1255
535
            val = val * 10 + (c - '0');
1256
535
            p++;
1257
535
            if (val > 255) return ipv4_fast_fail;
1258
535
          }
1259
750
        }
1260
812
      }
1261
1.62k
    }
1262
1263
1.46k
    ipv4 = (ipv4 << 8) | val;
1264
1265
1.46k
    if (i < 3) {
1266
1.27k
      if (p == pend || *p != '.') {
1267
510
        return ipv4_fast_fail;
1268
510
      }
1269
765
      p++;
1270
765
    }
1271
1.46k
  }
1272
1273
189
  if (p != pend) {
1274
34
    if (p == pend - 1 && *p == '.') {
1275
3
      return ipv4;
1276
3
    }
1277
31
    return ipv4_fast_fail;
1278
34
  }
1279
1280
155
  return ipv4;
1281
189
}
1282
1283
}  // namespace ada::checkers
1284
1285
#endif  // ADA_CHECKERS_INL_H
1286
/* end file include/ada/checkers-inl.h */
1287
/* begin file include/ada/log.h */
1288
/**
1289
 * @file log.h
1290
 * @brief Includes the definitions for logging.
1291
 * @private Excluded from docs through the doxygen file.
1292
 */
1293
#ifndef ADA_LOG_H
1294
#define ADA_LOG_H
1295
1296
// To enable logging, set ADA_LOGGING to 1:
1297
#ifndef ADA_LOGGING
1298
#define ADA_LOGGING 0
1299
#endif
1300
1301
#if ADA_LOGGING
1302
#include <iostream>
1303
#endif  // ADA_LOGGING
1304
1305
namespace ada {
1306
1307
/**
1308
 * Log a message. If you want to have no overhead when logging is disabled, use
1309
 * the ada_log macro.
1310
 * @private
1311
 */
1312
template <typename... Args>
1313
constexpr ada_really_inline void log([[maybe_unused]] Args... args) {
1314
#if ADA_LOGGING
1315
  ((std::cout << "ADA_LOG: ") << ... << args) << std::endl;
1316
#endif  // ADA_LOGGING
1317
}
1318
}  // namespace ada
1319
1320
#if ADA_LOGGING
1321
#ifndef ada_log
1322
#define ada_log(...)       \
1323
  do {                     \
1324
    ada::log(__VA_ARGS__); \
1325
  } while (0)
1326
#endif  // ada_log
1327
#else
1328
#define ada_log(...)
1329
#endif  // ADA_LOGGING
1330
1331
#endif  // ADA_LOG_H
1332
/* end file include/ada/log.h */
1333
/* begin file include/ada/encoding_type.h */
1334
/**
1335
 * @file encoding_type.h
1336
 * @brief Character encoding type definitions.
1337
 *
1338
 * Defines the encoding types supported for URL processing.
1339
 *
1340
 * @see https://encoding.spec.whatwg.org/
1341
 */
1342
#ifndef ADA_ENCODING_TYPE_H
1343
#define ADA_ENCODING_TYPE_H
1344
1345
#include <string>
1346
1347
namespace ada {
1348
1349
/**
1350
 * @brief Character encoding types for URL processing.
1351
 *
1352
 * Specifies the character encoding used for percent-decoding and other
1353
 * string operations. UTF-8 is the most commonly used encoding for URLs.
1354
 *
1355
 * @see https://encoding.spec.whatwg.org/#encodings
1356
 */
1357
enum class encoding_type {
1358
  UTF8,     /**< UTF-8 encoding (default for URLs) */
1359
  UTF_16LE, /**< UTF-16 Little Endian encoding */
1360
  UTF_16BE, /**< UTF-16 Big Endian encoding */
1361
};
1362
1363
/**
1364
 * Converts an encoding_type to its string representation.
1365
 * @param type The encoding type to convert.
1366
 * @return A string view of the encoding name.
1367
 */
1368
ada_warn_unused std::string_view to_string(encoding_type type);
1369
1370
}  // namespace ada
1371
1372
#endif  // ADA_ENCODING_TYPE_H
1373
/* end file include/ada/encoding_type.h */
1374
/* begin file include/ada/helpers.h */
1375
/**
1376
 * @file helpers.h
1377
 * @brief Definitions for helper functions used within Ada.
1378
 */
1379
#ifndef ADA_HELPERS_H
1380
#define ADA_HELPERS_H
1381
1382
/* begin file include/ada/url_base.h */
1383
/**
1384
 * @file url_base.h
1385
 * @brief Base class and common definitions for URL types.
1386
 *
1387
 * This file defines the `url_base` abstract base class from which both
1388
 * `ada::url` and `ada::url_aggregator` inherit. It also defines common
1389
 * enumerations like `url_host_type`.
1390
 */
1391
#ifndef ADA_URL_BASE_H
1392
#define ADA_URL_BASE_H
1393
1394
/* begin file include/ada/scheme.h */
1395
/**
1396
 * @file scheme.h
1397
 * @brief URL scheme type definitions and utilities.
1398
 *
1399
 * This header defines the URL scheme types (http, https, etc.) and provides
1400
 * functions to identify special schemes and their default ports according
1401
 * to the WHATWG URL Standard.
1402
 *
1403
 * @see https://url.spec.whatwg.org/#special-scheme
1404
 */
1405
#ifndef ADA_SCHEME_H
1406
#define ADA_SCHEME_H
1407
1408
1409
#include <string>
1410
1411
/**
1412
 * @namespace ada::scheme
1413
 * @brief URL scheme utilities and constants.
1414
 *
1415
 * Provides functions for working with URL schemes, including identification
1416
 * of special schemes and retrieval of default port numbers.
1417
 */
1418
namespace ada::scheme {
1419
1420
/**
1421
 * @brief Enumeration of URL scheme types.
1422
 *
1423
 * Special schemes have specific parsing rules and default ports.
1424
 * Using an enum allows efficient scheme comparisons without string operations.
1425
 *
1426
 * Default ports:
1427
 * - HTTP: 80
1428
 * - HTTPS: 443
1429
 * - WS: 80
1430
 * - WSS: 443
1431
 * - FTP: 21
1432
 * - FILE: (none)
1433
 */
1434
enum type : uint8_t {
1435
  HTTP = 0,        /**< http:// scheme (port 80) */
1436
  NOT_SPECIAL = 1, /**< Non-special scheme (no default port) */
1437
  HTTPS = 2,       /**< https:// scheme (port 443) */
1438
  WS = 3,          /**< ws:// WebSocket scheme (port 80) */
1439
  FTP = 4,         /**< ftp:// scheme (port 21) */
1440
  WSS = 5,         /**< wss:// secure WebSocket scheme (port 443) */
1441
  FILE = 6         /**< file:// scheme (no default port) */
1442
};
1443
1444
/**
1445
 * Checks if a scheme string is a special scheme.
1446
 * @param scheme The scheme string to check (e.g., "http", "https").
1447
 * @return `true` if the scheme is special, `false` otherwise.
1448
 * @see https://url.spec.whatwg.org/#special-scheme
1449
 */
1450
ada_really_inline constexpr bool is_special(std::string_view scheme);
1451
1452
/**
1453
 * Returns the default port for a special scheme string.
1454
 * @param scheme The scheme string (e.g., "http", "https").
1455
 * @return The default port number, or 0 if not a special scheme.
1456
 * @see https://url.spec.whatwg.org/#special-scheme
1457
 */
1458
constexpr uint16_t get_special_port(std::string_view scheme) noexcept;
1459
1460
/**
1461
 * Returns the default port for a scheme type.
1462
 * @param type The scheme type enum value.
1463
 * @return The default port number, or 0 if not applicable.
1464
 * @see https://url.spec.whatwg.org/#special-scheme
1465
 */
1466
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept;
1467
1468
/**
1469
 * Converts a scheme string to its type enum.
1470
 * @param scheme The scheme string to convert.
1471
 * @return The corresponding scheme type, or NOT_SPECIAL if not recognized.
1472
 */
1473
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept;
1474
1475
}  // namespace ada::scheme
1476
1477
#endif  // ADA_SCHEME_H
1478
/* end file include/ada/scheme.h */
1479
1480
#include <string>
1481
#include <string_view>
1482
1483
namespace ada {
1484
1485
/**
1486
 * @brief Enum representing the type of host in a URL.
1487
 *
1488
 * Used to distinguish between regular domain names, IPv4 addresses,
1489
 * and IPv6 addresses for proper parsing and serialization.
1490
 */
1491
enum url_host_type : uint8_t {
1492
  /** Regular domain name (e.g., "www.example.com") */
1493
  DEFAULT = 0,
1494
  /** IPv4 address (e.g., "127.0.0.1") */
1495
  IPV4 = 1,
1496
  /** IPv6 address (e.g., "[::1]" or "[2001:db8::1]") */
1497
  IPV6 = 2,
1498
};
1499
1500
/**
1501
 * @brief Abstract base class for URL representations.
1502
 *
1503
 * The `url_base` class provides the common interface and state shared by
1504
 * both `ada::url` and `ada::url_aggregator`. It contains basic URL attributes
1505
 * like validity status and scheme type, but delegates component storage and
1506
 * access to derived classes.
1507
 *
1508
 * @note This is an abstract class and cannot be instantiated directly.
1509
 *       Use `ada::url` or `ada::url_aggregator` instead.
1510
 *
1511
 * @see url
1512
 * @see url_aggregator
1513
 */
1514
struct url_base {
1515
4.72k
  virtual ~url_base() = default;
1516
1517
  /**
1518
   * Indicates whether the URL was successfully parsed.
1519
   * Set to `false` if parsing failed (e.g., invalid URL syntax).
1520
   */
1521
  bool is_valid{true};
1522
1523
  /**
1524
   * Indicates whether the URL has an opaque path (non-hierarchical).
1525
   * Opaque paths occur in non-special URLs like `mailto:` or `javascript:`.
1526
   */
1527
  bool has_opaque_path{false};
1528
1529
  /**
1530
   * The type of the URL's host (domain, IPv4, or IPv6).
1531
   */
1532
  url_host_type host_type = url_host_type::DEFAULT;
1533
1534
  /**
1535
   * @private
1536
   * Internal representation of the URL's scheme type.
1537
   */
1538
  ada::scheme::type type{ada::scheme::type::NOT_SPECIAL};
1539
1540
  /**
1541
   * Checks if the URL has a special scheme (http, https, ws, wss, ftp, file).
1542
   * Special schemes have specific parsing rules and default ports.
1543
   * @return `true` if the scheme is special, `false` otherwise.
1544
   */
1545
  [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept;
1546
1547
  /**
1548
   * Returns the URL's origin (scheme + host + port for special URLs).
1549
   * @return A newly allocated string containing the serialized origin.
1550
   * @see https://url.spec.whatwg.org/#concept-url-origin
1551
   */
1552
  [[nodiscard]] virtual std::string get_origin() const = 0;
1553
1554
  /**
1555
   * Validates whether the hostname is a valid domain according to RFC 1034.
1556
   * Checks that the domain and its labels have valid lengths.
1557
   * @return `true` if the domain is valid, `false` otherwise.
1558
   */
1559
  [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0;
1560
1561
  /**
1562
   * @private
1563
   * Returns the default port for special schemes (e.g., 443 for https).
1564
   * Returns 0 for file:// URLs or non-special schemes.
1565
   */
1566
  [[nodiscard]] inline uint16_t get_special_port() const noexcept;
1567
1568
  /**
1569
   * @private
1570
   * Returns the default port for the URL's scheme, or 0 if none.
1571
   */
1572
  [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept;
1573
1574
  /**
1575
   * @private
1576
   * Parses a port number from the input string.
1577
   * @param view The string containing the port to parse.
1578
   * @param check_trailing_content Whether to validate no trailing characters.
1579
   * @return Number of bytes consumed on success, 0 on failure.
1580
   */
1581
  virtual size_t parse_port(std::string_view view,
1582
                            bool check_trailing_content) = 0;
1583
1584
  /** @private */
1585
0
  virtual ada_really_inline size_t parse_port(std::string_view view) {
1586
0
    return this->parse_port(view, false);
1587
0
  }
1588
1589
  /**
1590
   * Returns a JSON string representation of this URL for debugging.
1591
   * @return A JSON-formatted string with URL information.
1592
   */
1593
  [[nodiscard]] virtual std::string to_string() const = 0;
1594
1595
  /** @private */
1596
  virtual inline void clear_pathname() = 0;
1597
1598
  /** @private */
1599
  virtual inline void clear_search() = 0;
1600
1601
  /** @private */
1602
  [[nodiscard]] virtual inline bool has_hash() const noexcept = 0;
1603
1604
  /** @private */
1605
  [[nodiscard]] virtual inline bool has_search() const noexcept = 0;
1606
1607
};  // url_base
1608
1609
}  // namespace ada
1610
1611
#endif
1612
/* end file include/ada/url_base.h */
1613
1614
#include <string>
1615
#include <string_view>
1616
#include <optional>
1617
1618
#if ADA_DEVELOPMENT_CHECKS
1619
#include <iostream>
1620
#endif  // ADA_DEVELOPMENT_CHECKS
1621
1622
/**
1623
 * These functions are not part of our public API and may
1624
 * change at any time.
1625
 *
1626
 * @private
1627
 * @namespace ada::helpers
1628
 * @brief Includes the definitions for helper functions
1629
 */
1630
namespace ada::helpers {
1631
1632
/**
1633
 * @private
1634
 */
1635
template <typename out_iter>
1636
void encode_json(std::string_view view, out_iter out);
1637
1638
/**
1639
 * @private
1640
 * This function is used to prune a fragment from a url, and returning the
1641
 * removed string if input has fragment.
1642
 *
1643
 * @details prune_hash seeks the first '#' and returns everything after it
1644
 * as a string_view, and modifies (in place) the input so that it points at
1645
 * everything before the '#'. If no '#' is found, the input is left unchanged
1646
 * and std::nullopt is returned.
1647
 *
1648
 * @attention The function is non-allocating and it does not throw.
1649
 * @returns Note that the returned string_view might be empty!
1650
 */
1651
ada_really_inline std::optional<std::string_view> prune_hash(
1652
    std::string_view& input) noexcept;
1653
1654
/**
1655
 * @private
1656
 * Defined by the URL specification, shorten a URLs paths.
1657
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1658
 * @returns Returns true if path is shortened.
1659
 */
1660
ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type);
1661
1662
/**
1663
 * @private
1664
 * Defined by the URL specification, shorten a URLs paths.
1665
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1666
 * @returns Returns true if path is shortened.
1667
 */
1668
ada_really_inline bool shorten_path(std::string_view& path,
1669
                                    ada::scheme::type type);
1670
1671
/**
1672
 * @private
1673
 *
1674
 * Parse the path from the provided input and append to the existing
1675
 * (possibly empty) path. The input cannot contain tabs and spaces: it
1676
 * is the user's responsibility to check.
1677
 *
1678
 * The input is expected to be UTF-8.
1679
 *
1680
 * @see https://url.spec.whatwg.org/
1681
 */
1682
ada_really_inline void parse_prepared_path(std::string_view input,
1683
                                           ada::scheme::type type,
1684
                                           std::string& path);
1685
1686
/**
1687
 * @private
1688
 * Remove and mutate all ASCII tab or newline characters from an input.
1689
 */
1690
ada_really_inline void remove_ascii_tab_or_newline(std::string& input);
1691
1692
/**
1693
 * @private
1694
 * Return the substring from input going from index pos to the end.
1695
 */
1696
ada_really_inline constexpr std::string_view substring(std::string_view input,
1697
                                                       size_t pos);
1698
1699
/**
1700
 * @private
1701
 * Returns true if the string_view points within the string.
1702
 */
1703
bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1704
1705
/**
1706
 * @private
1707
 * Return the substring from input going from index pos1 to the pos2 (non
1708
 * included). The length of the substring is pos2 - pos1.
1709
 */
1710
ada_really_inline constexpr std::string_view substring(std::string_view input,
1711
                                                       size_t pos1,
1712
2.15k
                                                       size_t pos2) {
1713
#if ADA_DEVELOPMENT_CHECKS
1714
  if (pos2 < pos1) {
1715
    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1716
              << std::endl;
1717
    abort();
1718
  }
1719
#endif
1720
2.15k
  return input.substr(pos1, pos2 - pos1);
1721
2.15k
}
1722
1723
/**
1724
 * @private
1725
 * Modify the string_view so that it has the new size pos, assuming that pos <=
1726
 * input.size(). This function cannot throw.
1727
 */
1728
ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1729
1730
/**
1731
 * @private
1732
 * Returns a host's delimiter location depending on the state of the instance,
1733
 * and whether a colon was found outside brackets. Used by the host parser.
1734
 */
1735
ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1736
    bool is_special, std::string_view& view) noexcept;
1737
1738
/**
1739
 * @private
1740
 * Removes leading and trailing C0 control and whitespace characters from
1741
 * string.
1742
 */
1743
void trim_c0_whitespace(std::string_view& input) noexcept;
1744
1745
/**
1746
 * @private
1747
 * @see
1748
 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1749
 */
1750
template <class url_type>
1751
ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url);
1752
1753
/**
1754
 * @private
1755
 * Finds the delimiter of a view in authority state.
1756
 */
1757
ada_really_inline size_t
1758
find_authority_delimiter_special(std::string_view view) noexcept;
1759
1760
/**
1761
 * @private
1762
 * Finds the delimiter of a view in authority state.
1763
 */
1764
ada_really_inline size_t
1765
find_authority_delimiter(std::string_view view) noexcept;
1766
1767
/**
1768
 * @private
1769
 */
1770
template <typename T, typename... Args>
1771
24
inline void inner_concat(std::string& buffer, T t) {
1772
24
  buffer.append(t);
1773
24
}
void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1771
24
inline void inner_concat(std::string& buffer, T t) {
1772
24
  buffer.append(t);
1773
24
}
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> >)
1774
1775
/**
1776
 * @private
1777
 */
1778
template <typename T, typename... Args>
1779
24
inline void inner_concat(std::string& buffer, T t, Args... args) {
1780
24
  buffer.append(t);
1781
24
  return inner_concat(buffer, args...);
1782
24
}
void ada::helpers::inner_concat<char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1779
24
inline void inner_concat(std::string& buffer, T t, Args... args) {
1780
24
  buffer.append(t);
1781
24
  return inner_concat(buffer, args...);
1782
24
}
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> >)
1783
1784
/**
1785
 * @private
1786
 * Concatenate the arguments and return a string.
1787
 * @returns a string
1788
 */
1789
template <typename... Args>
1790
24
std::string concat(Args... args) {
1791
24
  std::string answer;
1792
24
  inner_concat(answer, args...);
1793
24
  return answer;
1794
24
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1790
24
std::string concat(Args... args) {
1791
24
  std::string answer;
1792
24
  inner_concat(answer, args...);
1793
24
  return answer;
1794
24
}
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> >)
1795
1796
/**
1797
 * @private
1798
 * @return Number of leading zeroes.
1799
 */
1800
4.17k
inline int leading_zeroes(uint32_t input_num) noexcept {
1801
#if ADA_REGULAR_VISUAL_STUDIO
1802
  unsigned long leading_zero(0);
1803
  unsigned long in(input_num);
1804
  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
1805
#else
1806
4.17k
  return __builtin_clz(input_num);
1807
4.17k
#endif  // ADA_REGULAR_VISUAL_STUDIO
1808
4.17k
}
1809
1810
/**
1811
 * @private
1812
 * Counts the number of decimal digits necessary to represent x.
1813
 * faster than std::to_string(x).size().
1814
 * @return digit count
1815
 */
1816
24
inline int fast_digit_count(uint32_t x) noexcept {
1817
24
  auto int_log2 = [](uint32_t z) -> int {
1818
24
    return 31 - ada::helpers::leading_zeroes(z | 1);
1819
24
  };
1820
  // Compiles to very few instructions. Note that the
1821
  // table is static and thus effectively a constant.
1822
  // We leave it inside the function because it is meaningless
1823
  // outside of it (this comes at no performance cost).
1824
24
  const static uint64_t table[] = {
1825
24
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
1826
24
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
1827
24
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
1828
24
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
1829
24
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
1830
24
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
1831
24
      42949672960, 42949672960};
1832
24
  return int((x + table[int_log2(x)]) >> 32);
1833
24
}
1834
}  // namespace ada::helpers
1835
1836
#endif  // ADA_HELPERS_H
1837
/* end file include/ada/helpers.h */
1838
/* begin file include/ada/parser.h */
1839
/**
1840
 * @file parser.h
1841
 * @brief Low-level URL parsing functions.
1842
 *
1843
 * This header provides the internal URL parsing implementation. Most users
1844
 * should use `ada::parse()` from implementation.h instead of these functions
1845
 * directly.
1846
 *
1847
 * @see implementation.h for the recommended public API
1848
 */
1849
#ifndef ADA_PARSER_H
1850
#define ADA_PARSER_H
1851
1852
#include <string_view>
1853
#include <variant>
1854
1855
/* begin file include/ada/expected.h */
1856
/**
1857
 * @file expected.h
1858
 * @brief Definitions for std::expected
1859
 * @private Excluded from docs through the doxygen file.
1860
 */
1861
///
1862
// expected - An implementation of std::expected with extensions
1863
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
1864
//
1865
// Documentation available at http://tl.tartanllama.xyz/
1866
//
1867
// To the extent possible under law, the author(s) have dedicated all
1868
// copyright and related and neighboring rights to this software to the
1869
// public domain worldwide. This software is distributed without any warranty.
1870
//
1871
// You should have received a copy of the CC0 Public Domain Dedication
1872
// along with this software. If not, see
1873
// <http://creativecommons.org/publicdomain/zero/1.0/>.
1874
///
1875
1876
#ifndef TL_EXPECTED_HPP
1877
#define TL_EXPECTED_HPP
1878
1879
#define TL_EXPECTED_VERSION_MAJOR 1
1880
#define TL_EXPECTED_VERSION_MINOR 1
1881
#define TL_EXPECTED_VERSION_PATCH 0
1882
1883
#include <exception>
1884
#include <functional>
1885
#include <type_traits>
1886
#include <utility>
1887
1888
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
1889
#define TL_EXPECTED_EXCEPTIONS_ENABLED
1890
#endif
1891
1892
#if (defined(_MSC_VER) && _MSC_VER == 1900)
1893
#define TL_EXPECTED_MSVC2015
1894
#define TL_EXPECTED_MSVC2015_CONSTEXPR
1895
#else
1896
#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
1897
#endif
1898
1899
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
1900
     !defined(__clang__))
1901
#define TL_EXPECTED_GCC49
1902
#endif
1903
1904
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
1905
     !defined(__clang__))
1906
#define TL_EXPECTED_GCC54
1907
#endif
1908
1909
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
1910
     !defined(__clang__))
1911
#define TL_EXPECTED_GCC55
1912
#endif
1913
1914
#if !defined(TL_ASSERT)
1915
// can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
1916
#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
1917
#include <cassert>
1918
686
#define TL_ASSERT(x) assert(x)
1919
#else
1920
#define TL_ASSERT(x)
1921
#endif
1922
#endif
1923
1924
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
1925
     !defined(__clang__))
1926
// GCC < 5 doesn't support overloading on const&& for member functions
1927
1928
#define TL_EXPECTED_NO_CONSTRR
1929
// GCC < 5 doesn't support some standard C++11 type traits
1930
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1931
  std::has_trivial_copy_constructor<T>
1932
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1933
  std::has_trivial_copy_assign<T>
1934
1935
// This one will be different for GCC 5.7 if it's ever supported
1936
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1937
  std::is_trivially_destructible<T>
1938
1939
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
1940
// std::vector for non-copyable types
1941
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
1942
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
1943
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
1944
namespace tl {
1945
namespace detail {
1946
template <class T>
1947
struct is_trivially_copy_constructible
1948
    : std::is_trivially_copy_constructible<T> {};
1949
#ifdef _GLIBCXX_VECTOR
1950
template <class T, class A>
1951
struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
1952
#endif
1953
}  // namespace detail
1954
}  // namespace tl
1955
#endif
1956
1957
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1958
  tl::detail::is_trivially_copy_constructible<T>
1959
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1960
  std::is_trivially_copy_assignable<T>
1961
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1962
  std::is_trivially_destructible<T>
1963
#else
1964
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1965
  std::is_trivially_copy_constructible<T>
1966
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1967
  std::is_trivially_copy_assignable<T>
1968
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1969
  std::is_trivially_destructible<T>
1970
#endif
1971
1972
#if __cplusplus > 201103L
1973
#define TL_EXPECTED_CXX14
1974
#endif
1975
1976
#ifdef TL_EXPECTED_GCC49
1977
#define TL_EXPECTED_GCC49_CONSTEXPR
1978
#else
1979
#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
1980
#endif
1981
1982
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
1983
     defined(TL_EXPECTED_GCC49))
1984
#define TL_EXPECTED_11_CONSTEXPR
1985
#else
1986
#define TL_EXPECTED_11_CONSTEXPR constexpr
1987
#endif
1988
1989
namespace tl {
1990
template <class T, class E>
1991
class expected;
1992
1993
#ifndef TL_MONOSTATE_INPLACE_MUTEX
1994
#define TL_MONOSTATE_INPLACE_MUTEX
1995
class monostate {};
1996
1997
struct in_place_t {
1998
  explicit in_place_t() = default;
1999
};
2000
static constexpr in_place_t in_place{};
2001
#endif
2002
2003
template <class E>
2004
class unexpected {
2005
 public:
2006
  static_assert(!std::is_same<E, void>::value, "E must not be void");
2007
2008
  unexpected() = delete;
2009
  constexpr explicit unexpected(const E& e) : m_val(e) {}
2010
2011
7.14k
  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
2012
2013
  template <class... Args, typename std::enable_if<std::is_constructible<
2014
                               E, Args&&...>::value>::type* = nullptr>
2015
  constexpr explicit unexpected(Args&&... args)
2016
0
      : m_val(std::forward<Args>(args)...) {}
2017
  template <
2018
      class U, class... Args,
2019
      typename std::enable_if<std::is_constructible<
2020
          E, std::initializer_list<U>&, Args&&...>::value>::type* = nullptr>
2021
  constexpr explicit unexpected(std::initializer_list<U> l, Args&&... args)
2022
      : m_val(l, std::forward<Args>(args)...) {}
2023
2024
  constexpr const E& value() const& { return m_val; }
2025
3.57k
  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
2026
  TL_EXPECTED_11_CONSTEXPR E&& value() && { return std::move(m_val); }
2027
  constexpr const E&& value() const&& { return std::move(m_val); }
2028
2029
 private:
2030
  E m_val;
2031
};
2032
2033
#ifdef __cpp_deduction_guides
2034
template <class E>
2035
unexpected(E) -> unexpected<E>;
2036
#endif
2037
2038
template <class E>
2039
constexpr bool operator==(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2040
  return lhs.value() == rhs.value();
2041
}
2042
template <class E>
2043
constexpr bool operator!=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2044
  return lhs.value() != rhs.value();
2045
}
2046
template <class E>
2047
constexpr bool operator<(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2048
  return lhs.value() < rhs.value();
2049
}
2050
template <class E>
2051
constexpr bool operator<=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2052
  return lhs.value() <= rhs.value();
2053
}
2054
template <class E>
2055
constexpr bool operator>(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2056
  return lhs.value() > rhs.value();
2057
}
2058
template <class E>
2059
constexpr bool operator>=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2060
  return lhs.value() >= rhs.value();
2061
}
2062
2063
template <class E>
2064
unexpected<typename std::decay<E>::type> make_unexpected(E&& e) {
2065
  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
2066
}
2067
2068
struct unexpect_t {
2069
  unexpect_t() = default;
2070
};
2071
static constexpr unexpect_t unexpect{};
2072
2073
namespace detail {
2074
template <typename E>
2075
0
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E&& e) {
2076
0
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2077
0
  throw std::forward<E>(e);
2078
#else
2079
  (void)e;
2080
#ifdef _MSC_VER
2081
  __assume(0);
2082
#else
2083
  __builtin_unreachable();
2084
#endif
2085
#endif
2086
0
}
2087
2088
#ifndef TL_TRAITS_MUTEX
2089
#define TL_TRAITS_MUTEX
2090
// C++14-style aliases for brevity
2091
template <class T>
2092
using remove_const_t = typename std::remove_const<T>::type;
2093
template <class T>
2094
using remove_reference_t = typename std::remove_reference<T>::type;
2095
template <class T>
2096
using decay_t = typename std::decay<T>::type;
2097
template <bool E, class T = void>
2098
using enable_if_t = typename std::enable_if<E, T>::type;
2099
template <bool B, class T, class F>
2100
using conditional_t = typename std::conditional<B, T, F>::type;
2101
2102
// std::conjunction from C++17
2103
template <class...>
2104
struct conjunction : std::true_type {};
2105
template <class B>
2106
struct conjunction<B> : B {};
2107
template <class B, class... Bs>
2108
struct conjunction<B, Bs...>
2109
    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2110
2111
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2112
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2113
#endif
2114
2115
// In C++11 mode, there's an issue in libc++'s std::mem_fn
2116
// which results in a hard-error when using it in a noexcept expression
2117
// in some cases. This is a check to workaround the common failing case.
2118
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2119
template <class T>
2120
struct is_pointer_to_non_const_member_func : std::false_type {};
2121
template <class T, class Ret, class... Args>
2122
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
2123
    : std::true_type {};
2124
template <class T, class Ret, class... Args>
2125
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
2126
    : std::true_type {};
2127
template <class T, class Ret, class... Args>
2128
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
2129
    : std::true_type {};
2130
template <class T, class Ret, class... Args>
2131
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
2132
    : std::true_type {};
2133
template <class T, class Ret, class... Args>
2134
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&>
2135
    : std::true_type {};
2136
template <class T, class Ret, class... Args>
2137
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&>
2138
    : std::true_type {};
2139
2140
template <class T>
2141
struct is_const_or_const_ref : std::false_type {};
2142
template <class T>
2143
struct is_const_or_const_ref<T const&> : std::true_type {};
2144
template <class T>
2145
struct is_const_or_const_ref<T const> : std::true_type {};
2146
#endif
2147
2148
// std::invoke from C++17
2149
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2150
template <
2151
    typename Fn, typename... Args,
2152
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2153
    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2154
                             is_const_or_const_ref<Args...>::value)>,
2155
#endif
2156
    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2157
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2158
    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2159
    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2160
  return std::mem_fn(f)(std::forward<Args>(args)...);
2161
}
2162
2163
template <typename Fn, typename... Args,
2164
          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2165
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2166
    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2167
    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2168
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
2169
}
2170
2171
// std::invoke_result from C++17
2172
template <class F, class, class... Us>
2173
struct invoke_result_impl;
2174
2175
template <class F, class... Us>
2176
struct invoke_result_impl<
2177
    F,
2178
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2179
    Us...> {
2180
  using type =
2181
      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2182
};
2183
2184
template <class F, class... Us>
2185
using invoke_result = invoke_result_impl<F, void, Us...>;
2186
2187
template <class F, class... Us>
2188
using invoke_result_t = typename invoke_result<F, Us...>::type;
2189
2190
#if defined(_MSC_VER) && _MSC_VER <= 1900
2191
// TODO make a version which works with MSVC 2015
2192
template <class T, class U = T>
2193
struct is_swappable : std::true_type {};
2194
2195
template <class T, class U = T>
2196
struct is_nothrow_swappable : std::true_type {};
2197
#else
2198
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2199
namespace swap_adl_tests {
2200
// if swap ADL finds this then it would call std::swap otherwise (same
2201
// signature)
2202
struct tag {};
2203
2204
template <class T>
2205
tag swap(T&, T&);
2206
template <class T, std::size_t N>
2207
tag swap(T (&a)[N], T (&b)[N]);
2208
2209
// helper functions to test if an unqualified swap is possible, and if it
2210
// becomes std::swap
2211
template <class, class>
2212
std::false_type can_swap(...) noexcept(false);
2213
template <class T, class U,
2214
          class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
2215
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(),
2216
                                                    std::declval<U&>())));
2217
2218
template <class, class>
2219
std::false_type uses_std(...);
2220
template <class T, class U>
2221
std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag>
2222
uses_std(int);
2223
2224
template <class T>
2225
struct is_std_swap_noexcept
2226
    : std::integral_constant<bool,
2227
                             std::is_nothrow_move_constructible<T>::value &&
2228
                                 std::is_nothrow_move_assignable<T>::value> {};
2229
2230
template <class T, std::size_t N>
2231
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2232
2233
template <class T, class U>
2234
struct is_adl_swap_noexcept
2235
    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2236
}  // namespace swap_adl_tests
2237
2238
template <class T, class U = T>
2239
struct is_swappable
2240
    : std::integral_constant<
2241
          bool,
2242
          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2243
              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2244
               (std::is_move_assignable<T>::value &&
2245
                std::is_move_constructible<T>::value))> {};
2246
2247
template <class T, std::size_t N>
2248
struct is_swappable<T[N], T[N]>
2249
    : std::integral_constant<
2250
          bool,
2251
          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2252
              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2253
                   0))::value ||
2254
               is_swappable<T, T>::value)> {};
2255
2256
template <class T, class U = T>
2257
struct is_nothrow_swappable
2258
    : std::integral_constant<
2259
          bool,
2260
          is_swappable<T, U>::value &&
2261
              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2262
                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2263
               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2264
                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2265
#endif
2266
#endif
2267
2268
// Trait for checking if a type is a tl::expected
2269
template <class T>
2270
struct is_expected_impl : std::false_type {};
2271
template <class T, class E>
2272
struct is_expected_impl<expected<T, E>> : std::true_type {};
2273
template <class T>
2274
using is_expected = is_expected_impl<decay_t<T>>;
2275
2276
template <class T, class E, class U>
2277
using expected_enable_forward_value = detail::enable_if_t<
2278
    std::is_constructible<T, U&&>::value &&
2279
    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2280
    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2281
    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2282
2283
template <class T, class E, class U, class G, class UR, class GR>
2284
using expected_enable_from_other = detail::enable_if_t<
2285
    std::is_constructible<T, UR>::value &&
2286
    std::is_constructible<E, GR>::value &&
2287
    !std::is_constructible<T, expected<U, G>&>::value &&
2288
    !std::is_constructible<T, expected<U, G>&&>::value &&
2289
    !std::is_constructible<T, const expected<U, G>&>::value &&
2290
    !std::is_constructible<T, const expected<U, G>&&>::value &&
2291
    !std::is_convertible<expected<U, G>&, T>::value &&
2292
    !std::is_convertible<expected<U, G>&&, T>::value &&
2293
    !std::is_convertible<const expected<U, G>&, T>::value &&
2294
    !std::is_convertible<const expected<U, G>&&, T>::value>;
2295
2296
template <class T, class U>
2297
using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2298
2299
template <class T>
2300
using is_copy_constructible_or_void =
2301
    is_void_or<T, std::is_copy_constructible<T>>;
2302
2303
template <class T>
2304
using is_move_constructible_or_void =
2305
    is_void_or<T, std::is_move_constructible<T>>;
2306
2307
template <class T>
2308
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2309
2310
template <class T>
2311
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2312
2313
}  // namespace detail
2314
2315
namespace detail {
2316
struct no_init_t {};
2317
static constexpr no_init_t no_init{};
2318
2319
// Implements the storage of the values, and ensures that the destructor is
2320
// trivial if it can be.
2321
//
2322
// This specialization is for where neither `T` or `E` is trivially
2323
// destructible, so the destructors must be called on destruction of the
2324
// `expected`
2325
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2326
          bool = std::is_trivially_destructible<E>::value>
2327
struct expected_storage_base {
2328
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2329
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2330
2331
  template <class... Args,
2332
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2333
                nullptr>
2334
  constexpr expected_storage_base(in_place_t, Args&&... args)
2335
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2336
2337
  template <class U, class... Args,
2338
            detail::enable_if_t<std::is_constructible<
2339
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2340
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2341
                                  Args&&... args)
2342
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2343
  template <class... Args,
2344
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2345
                nullptr>
2346
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2347
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2348
2349
  template <class U, class... Args,
2350
            detail::enable_if_t<std::is_constructible<
2351
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2352
  constexpr explicit expected_storage_base(unexpect_t,
2353
                                           std::initializer_list<U> il,
2354
                                           Args&&... args)
2355
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2356
2357
  ~expected_storage_base() {
2358
    if (m_has_val) {
2359
      m_val.~T();
2360
    } else {
2361
      m_unexpect.~unexpected<E>();
2362
    }
2363
  }
2364
  union {
2365
    T m_val;
2366
    unexpected<E> m_unexpect;
2367
    char m_no_init;
2368
  };
2369
  bool m_has_val;
2370
};
2371
2372
// This specialization is for when both `T` and `E` are trivially-destructible,
2373
// so the destructor of the `expected` can be trivial.
2374
template <class T, class E>
2375
struct expected_storage_base<T, E, true, true> {
2376
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2377
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2378
2379
  template <class... Args,
2380
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2381
                nullptr>
2382
  constexpr expected_storage_base(in_place_t, Args&&... args)
2383
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_
2384
2385
  template <class U, class... Args,
2386
            detail::enable_if_t<std::is_constructible<
2387
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2388
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2389
                                  Args&&... args)
2390
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2391
  template <class... Args,
2392
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2393
                nullptr>
2394
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2395
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2396
2397
  template <class U, class... Args,
2398
            detail::enable_if_t<std::is_constructible<
2399
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2400
  constexpr explicit expected_storage_base(unexpect_t,
2401
                                           std::initializer_list<U> il,
2402
                                           Args&&... args)
2403
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2404
2405
  ~expected_storage_base() = default;
2406
  union {
2407
    T m_val;
2408
    unexpected<E> m_unexpect;
2409
    char m_no_init;
2410
  };
2411
  bool m_has_val;
2412
};
2413
2414
// T is trivial, E is not.
2415
template <class T, class E>
2416
struct expected_storage_base<T, E, true, false> {
2417
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2418
  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2419
      : m_no_init(), m_has_val(false) {}
2420
2421
  template <class... Args,
2422
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2423
                nullptr>
2424
  constexpr expected_storage_base(in_place_t, Args&&... args)
2425
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2426
2427
  template <class U, class... Args,
2428
            detail::enable_if_t<std::is_constructible<
2429
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2430
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2431
                                  Args&&... args)
2432
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2433
  template <class... Args,
2434
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2435
                nullptr>
2436
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2437
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2438
2439
  template <class U, class... Args,
2440
            detail::enable_if_t<std::is_constructible<
2441
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2442
  constexpr explicit expected_storage_base(unexpect_t,
2443
                                           std::initializer_list<U> il,
2444
                                           Args&&... args)
2445
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2446
2447
  ~expected_storage_base() {
2448
    if (!m_has_val) {
2449
      m_unexpect.~unexpected<E>();
2450
    }
2451
  }
2452
2453
  union {
2454
    T m_val;
2455
    unexpected<E> m_unexpect;
2456
    char m_no_init;
2457
  };
2458
  bool m_has_val;
2459
};
2460
2461
// E is trivial, T is not.
2462
template <class T, class E>
2463
struct expected_storage_base<T, E, false, true> {
2464
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2465
0
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2466
2467
  template <class... Args,
2468
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2469
                nullptr>
2470
  constexpr expected_storage_base(in_place_t, Args&&... args)
2471
576
      : 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
2471
576
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJRA1_KcETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJRS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
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_
2472
2473
  template <class U, class... Args,
2474
            detail::enable_if_t<std::is_constructible<
2475
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2476
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2477
                                  Args&&... args)
2478
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2479
  template <class... Args,
2480
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2481
                nullptr>
2482
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2483
3.57k
      : 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
2483
3.57k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
2484
2485
  template <class U, class... Args,
2486
            detail::enable_if_t<std::is_constructible<
2487
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2488
  constexpr explicit expected_storage_base(unexpect_t,
2489
                                           std::initializer_list<U> il,
2490
                                           Args&&... args)
2491
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2492
2493
4.14k
  ~expected_storage_base() {
2494
4.14k
    if (m_has_val) {
2495
576
      m_val.~T();
2496
576
    }
2497
4.14k
  }
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
2493
4.14k
  ~expected_storage_base() {
2494
4.14k
    if (m_has_val) {
2495
576
      m_val.~T();
2496
576
    }
2497
4.14k
  }
Unexecuted instantiation: tl::detail::expected_storage_base<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors, false, true>::~expected_storage_base()
Unexecuted instantiation: tl::detail::expected_storage_base<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()
2498
  union {
2499
    T m_val;
2500
    unexpected<E> m_unexpect;
2501
    char m_no_init;
2502
  };
2503
  bool m_has_val;
2504
};
2505
2506
// `T` is `void`, `E` is trivially-destructible
2507
template <class E>
2508
struct expected_storage_base<void, E, false, true> {
2509
#if __GNUC__ <= 5
2510
// no constexpr for GCC 4/5 bug
2511
#else
2512
  TL_EXPECTED_MSVC2015_CONSTEXPR
2513
#endif
2514
  expected_storage_base() : m_has_val(true) {}
2515
2516
  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2517
2518
  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2519
2520
  template <class... Args,
2521
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2522
                nullptr>
2523
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2524
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2525
2526
  template <class U, class... Args,
2527
            detail::enable_if_t<std::is_constructible<
2528
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2529
  constexpr explicit expected_storage_base(unexpect_t,
2530
                                           std::initializer_list<U> il,
2531
                                           Args&&... args)
2532
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2533
2534
  ~expected_storage_base() = default;
2535
  struct dummy {};
2536
  union {
2537
    unexpected<E> m_unexpect;
2538
    dummy m_val;
2539
  };
2540
  bool m_has_val;
2541
};
2542
2543
// `T` is `void`, `E` is not trivially-destructible
2544
template <class E>
2545
struct expected_storage_base<void, E, false, false> {
2546
  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2547
  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2548
2549
  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2550
2551
  template <class... Args,
2552
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2553
                nullptr>
2554
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2555
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2556
2557
  template <class U, class... Args,
2558
            detail::enable_if_t<std::is_constructible<
2559
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2560
  constexpr explicit expected_storage_base(unexpect_t,
2561
                                           std::initializer_list<U> il,
2562
                                           Args&&... args)
2563
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2564
2565
  ~expected_storage_base() {
2566
    if (!m_has_val) {
2567
      m_unexpect.~unexpected<E>();
2568
    }
2569
  }
2570
2571
  union {
2572
    unexpected<E> m_unexpect;
2573
    char m_dummy;
2574
  };
2575
  bool m_has_val;
2576
};
2577
2578
// This base class provides some handy member functions which can be used in
2579
// further derived classes
2580
template <class T, class E>
2581
struct expected_operations_base : expected_storage_base<T, E> {
2582
  using expected_storage_base<T, E>::expected_storage_base;
2583
2584
  template <class... Args>
2585
  void construct(Args&&... args) noexcept {
2586
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2587
    this->m_has_val = true;
2588
  }
2589
2590
  template <class Rhs>
2591
  // NOLINTNEXTLINE(bugprone-exception-escape)
2592
0
  void construct_with(Rhs&& rhs) noexcept {
2593
0
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2594
0
    this->m_has_val = true;
2595
0
  }
2596
2597
  template <class... Args>
2598
0
  void construct_error(Args&&... args) noexcept {
2599
0
    new (std::addressof(this->m_unexpect))
2600
0
        unexpected<E>(std::forward<Args>(args)...);
2601
0
    this->m_has_val = false;
2602
0
  }
2603
2604
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2605
2606
  // These assign overloads ensure that the most efficient assignment
2607
  // implementation is used while maintaining the strong exception guarantee.
2608
  // The problematic case is where rhs has a value, but *this does not.
2609
  //
2610
  // This overload handles the case where we can just copy-construct `T`
2611
  // directly into place without throwing.
2612
  template <class U = T,
2613
            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>* =
2614
                nullptr>
2615
  void assign(const expected_operations_base& rhs) noexcept {
2616
    if (!this->m_has_val && rhs.m_has_val) {
2617
      geterr().~unexpected<E>();
2618
      construct(rhs.get());
2619
    } else {
2620
      assign_common(rhs);
2621
    }
2622
  }
2623
2624
  // This overload handles the case where we can attempt to create a copy of
2625
  // `T`, then no-throw move it into place if the copy was successful.
2626
  template <class U = T,
2627
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2628
                                std::is_nothrow_move_constructible<U>::value>* =
2629
                nullptr>
2630
  void assign(const expected_operations_base& rhs) noexcept {
2631
    if (!this->m_has_val && rhs.m_has_val) {
2632
      T tmp = rhs.get();
2633
      geterr().~unexpected<E>();
2634
      construct(std::move(tmp));
2635
    } else {
2636
      assign_common(rhs);
2637
    }
2638
  }
2639
2640
  // This overload is the worst-case, where we have to move-construct the
2641
  // unexpected value into temporary storage, then try to copy the T into place.
2642
  // If the construction succeeds, then everything is fine, but if it throws,
2643
  // then we move the old unexpected value back into place before rethrowing the
2644
  // exception.
2645
  template <class U = T,
2646
            detail::enable_if_t<
2647
                !std::is_nothrow_copy_constructible<U>::value &&
2648
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2649
  void assign(const expected_operations_base& rhs) {
2650
    if (!this->m_has_val && rhs.m_has_val) {
2651
      auto tmp = std::move(geterr());
2652
      geterr().~unexpected<E>();
2653
2654
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2655
      try {
2656
        construct(rhs.get());
2657
      } catch (...) {
2658
        geterr() = std::move(tmp);
2659
        throw;
2660
      }
2661
#else
2662
      construct(rhs.get());
2663
#endif
2664
    } else {
2665
      assign_common(rhs);
2666
    }
2667
  }
2668
2669
  // These overloads do the same as above, but for rvalues
2670
  template <class U = T,
2671
            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>* =
2672
                nullptr>
2673
  void assign(expected_operations_base&& rhs) noexcept {
2674
    if (!this->m_has_val && rhs.m_has_val) {
2675
      geterr().~unexpected<E>();
2676
      construct(std::move(rhs).get());
2677
    } else {
2678
      assign_common(std::move(rhs));
2679
    }
2680
  }
2681
2682
  template <class U = T,
2683
            detail::enable_if_t<
2684
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2685
  void assign(expected_operations_base&& rhs) {
2686
    if (!this->m_has_val && rhs.m_has_val) {
2687
      auto tmp = std::move(geterr());
2688
      geterr().~unexpected<E>();
2689
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2690
      try {
2691
        construct(std::move(rhs).get());
2692
      } catch (...) {
2693
        geterr() = std::move(tmp);
2694
        throw;
2695
      }
2696
#else
2697
      construct(std::move(rhs).get());
2698
#endif
2699
    } else {
2700
      assign_common(std::move(rhs));
2701
    }
2702
  }
2703
2704
#else
2705
2706
  // If exceptions are disabled then we can just copy-construct
2707
  void assign(const expected_operations_base& rhs) noexcept {
2708
    if (!this->m_has_val && rhs.m_has_val) {
2709
      geterr().~unexpected<E>();
2710
      construct(rhs.get());
2711
    } else {
2712
      assign_common(rhs);
2713
    }
2714
  }
2715
2716
  void assign(expected_operations_base&& rhs) noexcept {
2717
    if (!this->m_has_val && rhs.m_has_val) {
2718
      geterr().~unexpected<E>();
2719
      construct(std::move(rhs).get());
2720
    } else {
2721
      assign_common(std::move(rhs));
2722
    }
2723
  }
2724
2725
#endif
2726
2727
  // The common part of move/copy assigning
2728
  template <class Rhs>
2729
  void assign_common(Rhs&& rhs) {
2730
    if (this->m_has_val) {
2731
      if (rhs.m_has_val) {
2732
        get() = std::forward<Rhs>(rhs).get();
2733
      } else {
2734
        destroy_val();
2735
        construct_error(std::forward<Rhs>(rhs).geterr());
2736
      }
2737
    } else {
2738
      if (!rhs.m_has_val) {
2739
        geterr() = std::forward<Rhs>(rhs).geterr();
2740
      }
2741
    }
2742
  }
2743
2744
0
  bool has_value() const { return this->m_has_val; }
2745
2746
  TL_EXPECTED_11_CONSTEXPR T& get() & { return this->m_val; }
2747
0
  constexpr const T& get() const& { return this->m_val; }
2748
  TL_EXPECTED_11_CONSTEXPR T&& get() && { return std::move(this->m_val); }
2749
#ifndef TL_EXPECTED_NO_CONSTRR
2750
  constexpr const T&& get() const&& { return std::move(this->m_val); }
2751
#endif
2752
2753
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2754
    return this->m_unexpect;
2755
  }
2756
0
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2757
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2758
    return std::move(this->m_unexpect);
2759
  }
2760
#ifndef TL_EXPECTED_NO_CONSTRR
2761
  constexpr const unexpected<E>&& geterr() const&& {
2762
    return std::move(this->m_unexpect);
2763
  }
2764
#endif
2765
2766
  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2767
};
2768
2769
// This base class provides some handy member functions which can be used in
2770
// further derived classes
2771
template <class E>
2772
struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2773
  using expected_storage_base<void, E>::expected_storage_base;
2774
2775
  template <class... Args>
2776
  void construct() noexcept {
2777
    this->m_has_val = true;
2778
  }
2779
2780
  // This function doesn't use its argument, but needs it so that code in
2781
  // levels above this can work independently of whether T is void
2782
  template <class Rhs>
2783
  void construct_with(Rhs&&) noexcept {
2784
    this->m_has_val = true;
2785
  }
2786
2787
  template <class... Args>
2788
  void construct_error(Args&&... args) noexcept {
2789
    new (std::addressof(this->m_unexpect))
2790
        unexpected<E>(std::forward<Args>(args)...);
2791
    this->m_has_val = false;
2792
  }
2793
2794
  template <class Rhs>
2795
  void assign(Rhs&& rhs) noexcept {
2796
    if (!this->m_has_val) {
2797
      if (rhs.m_has_val) {
2798
        geterr().~unexpected<E>();
2799
        construct();
2800
      } else {
2801
        geterr() = std::forward<Rhs>(rhs).geterr();
2802
      }
2803
    } else {
2804
      if (!rhs.m_has_val) {
2805
        construct_error(std::forward<Rhs>(rhs).geterr());
2806
      }
2807
    }
2808
  }
2809
2810
  bool has_value() const { return this->m_has_val; }
2811
2812
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2813
    return this->m_unexpect;
2814
  }
2815
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2816
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2817
    return std::move(this->m_unexpect);
2818
  }
2819
#ifndef TL_EXPECTED_NO_CONSTRR
2820
  constexpr const unexpected<E>&& geterr() const&& {
2821
    return std::move(this->m_unexpect);
2822
  }
2823
#endif
2824
2825
  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
2826
    // no-op
2827
  }
2828
};
2829
2830
// This class manages conditionally having a trivial copy constructor
2831
// This specialization is for when T and E are trivially copy constructible
2832
template <class T, class E,
2833
          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(
2834
                                   T)>::value &&
2835
                 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
2836
struct expected_copy_base : expected_operations_base<T, E> {
2837
  using expected_operations_base<T, E>::expected_operations_base;
2838
};
2839
2840
// This specialization is for when T or E are not trivially copy constructible
2841
template <class T, class E>
2842
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
2843
  using expected_operations_base<T, E>::expected_operations_base;
2844
2845
  expected_copy_base() = default;
2846
  expected_copy_base(const expected_copy_base& rhs)
2847
0
      : expected_operations_base<T, E>(no_init) {
2848
0
    if (rhs.has_value()) {
2849
0
      this->construct_with(rhs);
2850
0
    } else {
2851
0
      this->construct_error(rhs.geterr());
2852
0
    }
2853
0
  }
2854
2855
  expected_copy_base(expected_copy_base&& rhs) = default;
2856
  expected_copy_base& operator=(const expected_copy_base& rhs) = default;
2857
  expected_copy_base& operator=(expected_copy_base&& rhs) = default;
2858
};
2859
2860
// This class manages conditionally having a trivial move constructor
2861
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
2862
// doesn't implement an analogue to std::is_trivially_move_constructible. We
2863
// have to make do with a non-trivial move constructor even if T is trivially
2864
// move constructible
2865
#ifndef TL_EXPECTED_GCC49
2866
template <class T, class E,
2867
          bool =
2868
              is_void_or<T, std::is_trivially_move_constructible<T>>::value &&
2869
              std::is_trivially_move_constructible<E>::value>
2870
struct expected_move_base : expected_copy_base<T, E> {
2871
  using expected_copy_base<T, E>::expected_copy_base;
2872
};
2873
#else
2874
template <class T, class E, bool = false>
2875
struct expected_move_base;
2876
#endif
2877
template <class T, class E>
2878
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
2879
  using expected_copy_base<T, E>::expected_copy_base;
2880
2881
  expected_move_base() = default;
2882
0
  expected_move_base(const expected_move_base& rhs) = default;
2883
2884
  expected_move_base(expected_move_base&& rhs) noexcept(
2885
      std::is_nothrow_move_constructible<T>::value)
2886
      : expected_copy_base<T, E>(no_init) {
2887
    if (rhs.has_value()) {
2888
      this->construct_with(std::move(rhs));
2889
    } else {
2890
      this->construct_error(std::move(rhs.geterr()));
2891
    }
2892
  }
2893
  expected_move_base& operator=(const expected_move_base& rhs) = default;
2894
  expected_move_base& operator=(expected_move_base&& rhs) = default;
2895
};
2896
2897
// This class manages conditionally having a trivial copy assignment operator
2898
template <
2899
    class T, class E,
2900
    bool =
2901
        is_void_or<
2902
            T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
2903
                           TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
2904
                           TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value &&
2905
        TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value &&
2906
        TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value &&
2907
        TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
2908
struct expected_copy_assign_base : expected_move_base<T, E> {
2909
  using expected_move_base<T, E>::expected_move_base;
2910
};
2911
2912
template <class T, class E>
2913
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
2914
  using expected_move_base<T, E>::expected_move_base;
2915
2916
  expected_copy_assign_base() = default;
2917
0
  expected_copy_assign_base(const expected_copy_assign_base& rhs) = default;
2918
2919
  expected_copy_assign_base(expected_copy_assign_base&& rhs) = default;
2920
  expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
2921
    this->assign(rhs);
2922
    return *this;
2923
  }
2924
  expected_copy_assign_base& operator=(expected_copy_assign_base&& rhs) =
2925
      default;
2926
};
2927
2928
// This class manages conditionally having a trivial move assignment operator
2929
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
2930
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
2931
// to make do with a non-trivial move assignment operator even if T is trivially
2932
// move assignable
2933
#ifndef TL_EXPECTED_GCC49
2934
template <
2935
    class T, class E,
2936
    bool = is_void_or<
2937
               T, conjunction<std::is_trivially_destructible<T>,
2938
                              std::is_trivially_move_constructible<T>,
2939
                              std::is_trivially_move_assignable<T>>>::value &&
2940
           std::is_trivially_destructible<E>::value &&
2941
           std::is_trivially_move_constructible<E>::value &&
2942
           std::is_trivially_move_assignable<E>::value>
2943
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
2944
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
2945
};
2946
#else
2947
template <class T, class E, bool = false>
2948
struct expected_move_assign_base;
2949
#endif
2950
2951
template <class T, class E>
2952
struct expected_move_assign_base<T, E, false>
2953
    : expected_copy_assign_base<T, E> {
2954
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
2955
2956
  expected_move_assign_base() = default;
2957
0
  expected_move_assign_base(const expected_move_assign_base& rhs) = default;
2958
2959
  expected_move_assign_base(expected_move_assign_base&& rhs) = default;
2960
2961
  expected_move_assign_base& operator=(const expected_move_assign_base& rhs) =
2962
      default;
2963
2964
  expected_move_assign_base&
2965
  operator=(expected_move_assign_base&& rhs) noexcept(
2966
      std::is_nothrow_move_constructible<T>::value &&
2967
      std::is_nothrow_move_assignable<T>::value) {
2968
    this->assign(std::move(rhs));
2969
    return *this;
2970
  }
2971
};
2972
2973
// expected_delete_ctor_base will conditionally delete copy and move
2974
// constructors depending on whether T is copy/move constructible
2975
template <class T, class E,
2976
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
2977
                             std::is_copy_constructible<E>::value),
2978
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
2979
                             std::is_move_constructible<E>::value)>
2980
struct expected_delete_ctor_base {
2981
  expected_delete_ctor_base() = default;
2982
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
2983
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
2984
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
2985
      default;
2986
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
2987
      default;
2988
};
2989
2990
template <class T, class E>
2991
struct expected_delete_ctor_base<T, E, true, false> {
2992
  expected_delete_ctor_base() = default;
2993
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
2994
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
2995
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
2996
      default;
2997
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
2998
      default;
2999
};
3000
3001
template <class T, class E>
3002
struct expected_delete_ctor_base<T, E, false, true> {
3003
  expected_delete_ctor_base() = default;
3004
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3005
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3006
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3007
      default;
3008
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3009
      default;
3010
};
3011
3012
template <class T, class E>
3013
struct expected_delete_ctor_base<T, E, false, false> {
3014
  expected_delete_ctor_base() = default;
3015
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3016
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3017
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3018
      default;
3019
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3020
      default;
3021
};
3022
3023
// expected_delete_assign_base will conditionally delete copy and move
3024
// constructors depending on whether T and E are copy/move constructible +
3025
// assignable
3026
template <class T, class E,
3027
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3028
                             std::is_copy_constructible<E>::value &&
3029
                             is_copy_assignable_or_void<T>::value &&
3030
                             std::is_copy_assignable<E>::value),
3031
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3032
                             std::is_move_constructible<E>::value &&
3033
                             is_move_assignable_or_void<T>::value &&
3034
                             std::is_move_assignable<E>::value)>
3035
struct expected_delete_assign_base {
3036
  expected_delete_assign_base() = default;
3037
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3038
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3039
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3040
      default;
3041
  expected_delete_assign_base& operator=(
3042
      expected_delete_assign_base&&) noexcept = default;
3043
};
3044
3045
template <class T, class E>
3046
struct expected_delete_assign_base<T, E, true, false> {
3047
  expected_delete_assign_base() = default;
3048
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3049
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = 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 = default;
3061
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3062
      delete;
3063
  expected_delete_assign_base& operator=(
3064
      expected_delete_assign_base&&) noexcept = default;
3065
};
3066
3067
template <class T, class E>
3068
struct expected_delete_assign_base<T, E, false, false> {
3069
  expected_delete_assign_base() = default;
3070
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3071
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3072
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3073
      delete;
3074
  expected_delete_assign_base& operator=(
3075
      expected_delete_assign_base&&) noexcept = delete;
3076
};
3077
3078
// This is needed to be able to construct the expected_default_ctor_base which
3079
// follows, while still conditionally deleting the default constructor.
3080
struct default_constructor_tag {
3081
  explicit constexpr default_constructor_tag() = default;
3082
};
3083
3084
// expected_default_ctor_base will ensure that expected has a deleted default
3085
// consturctor if T is not default constructible.
3086
// This specialization is for when T is default constructible
3087
template <class T, class E,
3088
          bool Enable =
3089
              std::is_default_constructible<T>::value || std::is_void<T>::value>
3090
struct expected_default_ctor_base {
3091
  constexpr expected_default_ctor_base() noexcept = default;
3092
  constexpr expected_default_ctor_base(
3093
      expected_default_ctor_base const&) noexcept = default;
3094
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3095
      default;
3096
  expected_default_ctor_base& operator=(
3097
      expected_default_ctor_base const&) noexcept = default;
3098
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3099
      default;
3100
3101
4.14k
  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
3101
4.14k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_pattern_init, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_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)
3102
};
3103
3104
// This specialization is for when T is not default constructible
3105
template <class T, class E>
3106
struct expected_default_ctor_base<T, E, false> {
3107
  constexpr expected_default_ctor_base() noexcept = delete;
3108
  constexpr expected_default_ctor_base(
3109
      expected_default_ctor_base const&) noexcept = default;
3110
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3111
      default;
3112
  expected_default_ctor_base& operator=(
3113
      expected_default_ctor_base const&) noexcept = default;
3114
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3115
      default;
3116
3117
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3118
};
3119
}  // namespace detail
3120
3121
template <class E>
3122
class bad_expected_access : public std::exception {
3123
 public:
3124
0
  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3125
3126
0
  virtual const char* what() const noexcept override {
3127
0
    return "Bad expected access";
3128
0
  }
3129
3130
  const E& error() const& { return m_val; }
3131
  E& error() & { return m_val; }
3132
  const E&& error() const&& { return std::move(m_val); }
3133
  E&& error() && { return std::move(m_val); }
3134
3135
 private:
3136
  E m_val;
3137
};
3138
3139
/// An `expected<T, E>` object is an object that contains the storage for
3140
/// another object and manages the lifetime of this contained object `T`.
3141
/// Alternatively it could contain the storage for another unexpected object
3142
/// `E`. The contained object may not be initialized after the expected object
3143
/// has been initialized, and may not be destroyed before the expected object
3144
/// has been destroyed. The initialization state of the contained object is
3145
/// tracked by the expected object.
3146
template <class T, class E>
3147
class expected : private detail::expected_move_assign_base<T, E>,
3148
                 private detail::expected_delete_ctor_base<T, E>,
3149
                 private detail::expected_delete_assign_base<T, E>,
3150
                 private detail::expected_default_ctor_base<T, E> {
3151
  static_assert(!std::is_reference<T>::value, "T must not be a reference");
3152
  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3153
                "T must not be in_place_t");
3154
  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3155
                "T must not be unexpect_t");
3156
  static_assert(
3157
      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3158
      "T must not be unexpected<E>");
3159
  static_assert(!std::is_reference<E>::value, "E must not be a reference");
3160
3161
686
  T* valptr() { return std::addressof(this->m_val); }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::valptr()
tl::expected<ada::url_aggregator, ada::errors>::valptr()
Line
Count
Source
3161
686
  T* valptr() { return std::addressof(this->m_val); }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::valptr()
3162
  const T* valptr() const { return std::addressof(this->m_val); }
3163
  unexpected<E>* errptr() { return std::addressof(this->m_unexpect); }
3164
  const unexpected<E>* errptr() const {
3165
    return std::addressof(this->m_unexpect);
3166
  }
3167
3168
  template <class U = T,
3169
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3170
0
  TL_EXPECTED_11_CONSTEXPR U& val() {
3171
0
    return this->m_val;
3172
0
  }
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEE3valIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
3173
0
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::err()
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::err()
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::err()
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::err()
3174
3175
  template <class U = T,
3176
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3177
  constexpr const U& val() const {
3178
    return this->m_val;
3179
  }
3180
  constexpr const unexpected<E>& err() const { return this->m_unexpect; }
3181
3182
  using impl_base = detail::expected_move_assign_base<T, E>;
3183
  using ctor_base = detail::expected_default_ctor_base<T, E>;
3184
3185
 public:
3186
  typedef T value_type;
3187
  typedef E error_type;
3188
  typedef unexpected<E> unexpected_type;
3189
3190
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3191
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3192
  template <class F>
3193
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & {
3194
    return and_then_impl(*this, std::forward<F>(f));
3195
  }
3196
  template <class F>
3197
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && {
3198
    return and_then_impl(std::move(*this), std::forward<F>(f));
3199
  }
3200
  template <class F>
3201
  constexpr auto and_then(F&& f) const& {
3202
    return and_then_impl(*this, std::forward<F>(f));
3203
  }
3204
3205
#ifndef TL_EXPECTED_NO_CONSTRR
3206
  template <class F>
3207
  constexpr auto and_then(F&& f) const&& {
3208
    return and_then_impl(std::move(*this), std::forward<F>(f));
3209
  }
3210
#endif
3211
3212
#else
3213
  template <class F>
3214
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & -> decltype(and_then_impl(
3215
      std::declval<expected&>(), std::forward<F>(f))) {
3216
    return and_then_impl(*this, std::forward<F>(f));
3217
  }
3218
  template <class F>
3219
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && -> decltype(and_then_impl(
3220
      std::declval<expected&&>(), std::forward<F>(f))) {
3221
    return and_then_impl(std::move(*this), std::forward<F>(f));
3222
  }
3223
  template <class F>
3224
  constexpr auto and_then(F&& f) const& -> decltype(and_then_impl(
3225
      std::declval<expected const&>(), std::forward<F>(f))) {
3226
    return and_then_impl(*this, std::forward<F>(f));
3227
  }
3228
3229
#ifndef TL_EXPECTED_NO_CONSTRR
3230
  template <class F>
3231
  constexpr auto and_then(F&& f) const&& -> decltype(and_then_impl(
3232
      std::declval<expected const&&>(), std::forward<F>(f))) {
3233
    return and_then_impl(std::move(*this), std::forward<F>(f));
3234
  }
3235
#endif
3236
#endif
3237
3238
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3239
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3240
  template <class F>
3241
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) & {
3242
    return expected_map_impl(*this, std::forward<F>(f));
3243
  }
3244
  template <class F>
3245
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) && {
3246
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3247
  }
3248
  template <class F>
3249
  constexpr auto map(F&& f) const& {
3250
    return expected_map_impl(*this, std::forward<F>(f));
3251
  }
3252
  template <class F>
3253
  constexpr auto map(F&& f) const&& {
3254
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3255
  }
3256
#else
3257
  template <class F>
3258
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3259
      std::declval<expected&>(), std::declval<F&&>())) map(F&& f) & {
3260
    return expected_map_impl(*this, std::forward<F>(f));
3261
  }
3262
  template <class F>
3263
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3264
                                                      std::declval<F&&>()))
3265
  map(F&& f) && {
3266
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3267
  }
3268
  template <class F>
3269
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3270
                                       std::declval<F&&>()))
3271
  map(F&& f) const& {
3272
    return expected_map_impl(*this, std::forward<F>(f));
3273
  }
3274
3275
#ifndef TL_EXPECTED_NO_CONSTRR
3276
  template <class F>
3277
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3278
                                       std::declval<F&&>())) map(F&& f)
3279
      const&& {
3280
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3281
  }
3282
#endif
3283
#endif
3284
3285
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3286
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3287
  template <class F>
3288
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) & {
3289
    return expected_map_impl(*this, std::forward<F>(f));
3290
  }
3291
  template <class F>
3292
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) && {
3293
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3294
  }
3295
  template <class F>
3296
  constexpr auto transform(F&& f) const& {
3297
    return expected_map_impl(*this, std::forward<F>(f));
3298
  }
3299
  template <class F>
3300
  constexpr auto transform(F&& f) const&& {
3301
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3302
  }
3303
#else
3304
  template <class F>
3305
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3306
      std::declval<expected&>(), std::declval<F&&>())) transform(F&& f) & {
3307
    return expected_map_impl(*this, std::forward<F>(f));
3308
  }
3309
  template <class F>
3310
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3311
                                                      std::declval<F&&>()))
3312
  transform(F&& f) && {
3313
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3314
  }
3315
  template <class F>
3316
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3317
                                       std::declval<F&&>()))
3318
  transform(F&& f) const& {
3319
    return expected_map_impl(*this, std::forward<F>(f));
3320
  }
3321
3322
#ifndef TL_EXPECTED_NO_CONSTRR
3323
  template <class F>
3324
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3325
                                       std::declval<F&&>())) transform(F&& f)
3326
      const&& {
3327
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3328
  }
3329
#endif
3330
#endif
3331
3332
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3333
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3334
  template <class F>
3335
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) & {
3336
    return map_error_impl(*this, std::forward<F>(f));
3337
  }
3338
  template <class F>
3339
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) && {
3340
    return map_error_impl(std::move(*this), std::forward<F>(f));
3341
  }
3342
  template <class F>
3343
  constexpr auto map_error(F&& f) const& {
3344
    return map_error_impl(*this, std::forward<F>(f));
3345
  }
3346
  template <class F>
3347
  constexpr auto map_error(F&& f) const&& {
3348
    return map_error_impl(std::move(*this), std::forward<F>(f));
3349
  }
3350
#else
3351
  template <class F>
3352
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3353
      std::declval<expected&>(), std::declval<F&&>())) map_error(F&& f) & {
3354
    return map_error_impl(*this, std::forward<F>(f));
3355
  }
3356
  template <class F>
3357
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3358
                                                   std::declval<F&&>()))
3359
  map_error(F&& f) && {
3360
    return map_error_impl(std::move(*this), std::forward<F>(f));
3361
  }
3362
  template <class F>
3363
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3364
                                    std::declval<F&&>()))
3365
  map_error(F&& f) const& {
3366
    return map_error_impl(*this, std::forward<F>(f));
3367
  }
3368
3369
#ifndef TL_EXPECTED_NO_CONSTRR
3370
  template <class F>
3371
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3372
                                    std::declval<F&&>())) map_error(F&& f)
3373
      const&& {
3374
    return map_error_impl(std::move(*this), std::forward<F>(f));
3375
  }
3376
#endif
3377
#endif
3378
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3379
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3380
  template <class F>
3381
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) & {
3382
    return map_error_impl(*this, std::forward<F>(f));
3383
  }
3384
  template <class F>
3385
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) && {
3386
    return map_error_impl(std::move(*this), std::forward<F>(f));
3387
  }
3388
  template <class F>
3389
  constexpr auto transform_error(F&& f) const& {
3390
    return map_error_impl(*this, std::forward<F>(f));
3391
  }
3392
  template <class F>
3393
  constexpr auto transform_error(F&& f) const&& {
3394
    return map_error_impl(std::move(*this), std::forward<F>(f));
3395
  }
3396
#else
3397
  template <class F>
3398
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3399
      std::declval<expected&>(),
3400
      std::declval<F&&>())) transform_error(F&& f) & {
3401
    return map_error_impl(*this, std::forward<F>(f));
3402
  }
3403
  template <class F>
3404
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3405
                                                   std::declval<F&&>()))
3406
  transform_error(F&& f) && {
3407
    return map_error_impl(std::move(*this), std::forward<F>(f));
3408
  }
3409
  template <class F>
3410
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3411
                                    std::declval<F&&>()))
3412
  transform_error(F&& f) const& {
3413
    return map_error_impl(*this, std::forward<F>(f));
3414
  }
3415
3416
#ifndef TL_EXPECTED_NO_CONSTRR
3417
  template <class F>
3418
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3419
                                    std::declval<F&&>())) transform_error(F&& f)
3420
      const&& {
3421
    return map_error_impl(std::move(*this), std::forward<F>(f));
3422
  }
3423
#endif
3424
#endif
3425
  template <class F>
3426
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) & {
3427
    return or_else_impl(*this, std::forward<F>(f));
3428
  }
3429
3430
  template <class F>
3431
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) && {
3432
    return or_else_impl(std::move(*this), std::forward<F>(f));
3433
  }
3434
3435
  template <class F>
3436
  expected constexpr or_else(F&& f) const& {
3437
    return or_else_impl(*this, std::forward<F>(f));
3438
  }
3439
3440
#ifndef TL_EXPECTED_NO_CONSTRR
3441
  template <class F>
3442
  expected constexpr or_else(F&& f) const&& {
3443
    return or_else_impl(std::move(*this), std::forward<F>(f));
3444
  }
3445
#endif
3446
  constexpr expected() = default;
3447
0
  constexpr expected(const expected& rhs) = default;
3448
  constexpr expected(expected&& rhs) = default;
3449
  expected& operator=(const expected& rhs) = default;
3450
  expected& operator=(expected&& rhs) = default;
3451
3452
  template <class... Args,
3453
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
3454
                nullptr>
3455
  constexpr expected(in_place_t, Args&&... args)
3456
576
      : impl_base(in_place, std::forward<Args>(args)...),
3457
576
        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
3456
576
      : impl_base(in_place, std::forward<Args>(args)...),
3457
576
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA1_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IJRS8_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
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_
3458
3459
  template <class U, class... Args,
3460
            detail::enable_if_t<std::is_constructible<
3461
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3462
  constexpr expected(in_place_t, std::initializer_list<U> il, Args&&... args)
3463
      : impl_base(in_place, il, std::forward<Args>(args)...),
3464
        ctor_base(detail::default_constructor_tag{}) {}
3465
3466
  template <
3467
      class G = E,
3468
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3469
      detail::enable_if_t<!std::is_convertible<const G&, E>::value>* = nullptr>
3470
  explicit constexpr expected(const unexpected<G>& e)
3471
      : impl_base(unexpect, e.value()),
3472
        ctor_base(detail::default_constructor_tag{}) {}
3473
3474
  template <
3475
      class G = E,
3476
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3477
      detail::enable_if_t<std::is_convertible<const G&, E>::value>* = nullptr>
3478
  constexpr expected(unexpected<G> const& e)
3479
      : impl_base(unexpect, e.value()),
3480
        ctor_base(detail::default_constructor_tag{}) {}
3481
3482
  template <
3483
      class G = E,
3484
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3485
      detail::enable_if_t<!std::is_convertible<G&&, E>::value>* = nullptr>
3486
  explicit constexpr expected(unexpected<G>&& e) noexcept(
3487
      std::is_nothrow_constructible<E, G&&>::value)
3488
      : impl_base(unexpect, std::move(e.value())),
3489
        ctor_base(detail::default_constructor_tag{}) {}
3490
3491
  template <
3492
      class G = E,
3493
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3494
      detail::enable_if_t<std::is_convertible<G&&, E>::value>* = nullptr>
3495
  constexpr expected(unexpected<G>&& e) noexcept(
3496
      std::is_nothrow_constructible<E, G&&>::value)
3497
3.57k
      : impl_base(unexpect, std::move(e.value())),
3498
3.57k
        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
3497
3.57k
      : impl_base(unexpect, std::move(e.value())),
3498
3.57k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
3499
3500
  template <class... Args,
3501
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
3502
                nullptr>
3503
  constexpr explicit expected(unexpect_t, Args&&... args)
3504
      : impl_base(unexpect, std::forward<Args>(args)...),
3505
        ctor_base(detail::default_constructor_tag{}) {}
3506
3507
  template <class U, class... Args,
3508
            detail::enable_if_t<std::is_constructible<
3509
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3510
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3511
                              Args&&... args)
3512
      : impl_base(unexpect, il, std::forward<Args>(args)...),
3513
        ctor_base(detail::default_constructor_tag{}) {}
3514
3515
  template <class U, class G,
3516
            detail::enable_if_t<!(std::is_convertible<U const&, T>::value &&
3517
                                  std::is_convertible<G const&, E>::value)>* =
3518
                nullptr,
3519
            detail::expected_enable_from_other<T, E, U, G, const U&,
3520
                                               const G&>* = nullptr>
3521
  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3522
      : ctor_base(detail::default_constructor_tag{}) {
3523
    if (rhs.has_value()) {
3524
      this->construct(*rhs);
3525
    } else {
3526
      this->construct_error(rhs.error());
3527
    }
3528
  }
3529
3530
  template <
3531
      class U, class G,
3532
      detail::enable_if_t<(std::is_convertible<U const&, T>::value &&
3533
                           std::is_convertible<G const&, E>::value)>* = nullptr,
3534
      detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* =
3535
          nullptr>
3536
  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3537
      : ctor_base(detail::default_constructor_tag{}) {
3538
    if (rhs.has_value()) {
3539
      this->construct(*rhs);
3540
    } else {
3541
      this->construct_error(rhs.error());
3542
    }
3543
  }
3544
3545
  template <
3546
      class U, class G,
3547
      detail::enable_if_t<!(std::is_convertible<U&&, T>::value &&
3548
                            std::is_convertible<G&&, E>::value)>* = nullptr,
3549
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3550
  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3551
      : ctor_base(detail::default_constructor_tag{}) {
3552
    if (rhs.has_value()) {
3553
      this->construct(std::move(*rhs));
3554
    } else {
3555
      this->construct_error(std::move(rhs.error()));
3556
    }
3557
  }
3558
3559
  template <
3560
      class U, class G,
3561
      detail::enable_if_t<(std::is_convertible<U&&, T>::value &&
3562
                           std::is_convertible<G&&, E>::value)>* = nullptr,
3563
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3564
  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3565
      : ctor_base(detail::default_constructor_tag{}) {
3566
    if (rhs.has_value()) {
3567
      this->construct(std::move(*rhs));
3568
    } else {
3569
      this->construct_error(std::move(rhs.error()));
3570
    }
3571
  }
3572
3573
  template <class U = T,
3574
            detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr,
3575
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3576
  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3577
      : expected(in_place, std::forward<U>(v)) {}
3578
3579
  template <class U = T,
3580
            detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr,
3581
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3582
  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3583
576
      : 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
3583
576
      : expected(in_place, std::forward<U>(v)) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS7_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS7_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESL_EE5valueEvE4typeELSI_0EEESE_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IRA1_KcTnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleIS7_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SO_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESO_EE5valueEvE4typeELSL_0EEESH_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IRS8_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S8_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS8_SF_EE5valuentsr3std7is_sameINS1_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SM_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESM_EE5valueEvE4typeELSJ_0EEESF_
Unexecuted instantiation: _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_
3584
3585
  template <
3586
      class U = T, class G = T,
3587
      detail::enable_if_t<std::is_nothrow_constructible<T, U&&>::value>* =
3588
          nullptr,
3589
      detail::enable_if_t<!std::is_void<G>::value>* = nullptr,
3590
      detail::enable_if_t<
3591
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3592
           !detail::conjunction<std::is_scalar<T>,
3593
                                std::is_same<T, detail::decay_t<U>>>::value &&
3594
           std::is_constructible<T, U>::value &&
3595
           std::is_assignable<G&, U>::value &&
3596
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3597
  expected& operator=(U&& v) {
3598
    if (has_value()) {
3599
      val() = std::forward<U>(v);
3600
    } else {
3601
      err().~unexpected<E>();
3602
      ::new (valptr()) T(std::forward<U>(v));
3603
      this->m_has_val = true;
3604
    }
3605
3606
    return *this;
3607
  }
3608
3609
  template <
3610
      class U = T, class G = T,
3611
      detail::enable_if_t<!std::is_nothrow_constructible<T, U&&>::value>* =
3612
          nullptr,
3613
      detail::enable_if_t<!std::is_void<U>::value>* = nullptr,
3614
      detail::enable_if_t<
3615
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3616
           !detail::conjunction<std::is_scalar<T>,
3617
                                std::is_same<T, detail::decay_t<U>>>::value &&
3618
           std::is_constructible<T, U>::value &&
3619
           std::is_assignable<G&, U>::value &&
3620
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3621
  expected& operator=(U&& v) {
3622
    if (has_value()) {
3623
      val() = std::forward<U>(v);
3624
    } else {
3625
      auto tmp = std::move(err());
3626
      err().~unexpected<E>();
3627
3628
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3629
      try {
3630
        ::new (valptr()) T(std::forward<U>(v));
3631
        this->m_has_val = true;
3632
      } catch (...) {
3633
        err() = std::move(tmp);
3634
        throw;
3635
      }
3636
#else
3637
      ::new (valptr()) T(std::forward<U>(v));
3638
      this->m_has_val = true;
3639
#endif
3640
    }
3641
3642
    return *this;
3643
  }
3644
3645
  template <class G = E,
3646
            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3647
                                std::is_assignable<G&, G>::value>* = nullptr>
3648
  expected& operator=(const unexpected<G>& rhs) {
3649
    if (!has_value()) {
3650
      err() = rhs;
3651
    } else {
3652
      this->destroy_val();
3653
      ::new (errptr()) unexpected<E>(rhs);
3654
      this->m_has_val = false;
3655
    }
3656
3657
    return *this;
3658
  }
3659
3660
  template <class G = E,
3661
            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3662
                                std::is_move_assignable<G>::value>* = nullptr>
3663
  expected& operator=(unexpected<G>&& rhs) noexcept {
3664
    if (!has_value()) {
3665
      err() = std::move(rhs);
3666
    } else {
3667
      this->destroy_val();
3668
      ::new (errptr()) unexpected<E>(std::move(rhs));
3669
      this->m_has_val = false;
3670
    }
3671
3672
    return *this;
3673
  }
3674
3675
  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3676
                               T, Args&&...>::value>* = nullptr>
3677
  void emplace(Args&&... args) {
3678
    if (has_value()) {
3679
      val().~T();
3680
    } else {
3681
      err().~unexpected<E>();
3682
      this->m_has_val = true;
3683
    }
3684
    ::new (valptr()) T(std::forward<Args>(args)...);
3685
  }
3686
3687
  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3688
                               T, Args&&...>::value>* = nullptr>
3689
  void emplace(Args&&... args) {
3690
    if (has_value()) {
3691
      val().~T();
3692
      ::new (valptr()) T(std::forward<Args>(args)...);
3693
    } else {
3694
      auto tmp = std::move(err());
3695
      err().~unexpected<E>();
3696
3697
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3698
      try {
3699
        ::new (valptr()) T(std::forward<Args>(args)...);
3700
        this->m_has_val = true;
3701
      } catch (...) {
3702
        err() = std::move(tmp);
3703
        throw;
3704
      }
3705
#else
3706
      ::new (valptr()) T(std::forward<Args>(args)...);
3707
      this->m_has_val = true;
3708
#endif
3709
    }
3710
  }
3711
3712
  template <class U, class... Args,
3713
            detail::enable_if_t<std::is_nothrow_constructible<
3714
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3715
  void emplace(std::initializer_list<U> il, Args&&... args) {
3716
    if (has_value()) {
3717
      T t(il, std::forward<Args>(args)...);
3718
      val() = std::move(t);
3719
    } else {
3720
      err().~unexpected<E>();
3721
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3722
      this->m_has_val = true;
3723
    }
3724
  }
3725
3726
  template <class U, class... Args,
3727
            detail::enable_if_t<!std::is_nothrow_constructible<
3728
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3729
  void emplace(std::initializer_list<U> il, Args&&... args) {
3730
    if (has_value()) {
3731
      T t(il, std::forward<Args>(args)...);
3732
      val() = std::move(t);
3733
    } else {
3734
      auto tmp = std::move(err());
3735
      err().~unexpected<E>();
3736
3737
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3738
      try {
3739
        ::new (valptr()) T(il, std::forward<Args>(args)...);
3740
        this->m_has_val = true;
3741
      } catch (...) {
3742
        err() = std::move(tmp);
3743
        throw;
3744
      }
3745
#else
3746
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3747
      this->m_has_val = true;
3748
#endif
3749
    }
3750
  }
3751
3752
 private:
3753
  using t_is_void = std::true_type;
3754
  using t_is_not_void = std::false_type;
3755
  using t_is_nothrow_move_constructible = std::true_type;
3756
  using move_constructing_t_can_throw = std::false_type;
3757
  using e_is_nothrow_move_constructible = std::true_type;
3758
  using move_constructing_e_can_throw = std::false_type;
3759
3760
  void swap_where_both_have_value(expected& /*rhs*/, t_is_void) noexcept {
3761
    // swapping void is a no-op
3762
  }
3763
3764
  void swap_where_both_have_value(expected& rhs, t_is_not_void) {
3765
    using std::swap;
3766
    swap(val(), rhs.val());
3767
  }
3768
3769
  void swap_where_only_one_has_value(expected& rhs, t_is_void) noexcept(
3770
      std::is_nothrow_move_constructible<E>::value) {
3771
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3772
    rhs.err().~unexpected_type();
3773
    std::swap(this->m_has_val, rhs.m_has_val);
3774
  }
3775
3776
  void swap_where_only_one_has_value(expected& rhs, t_is_not_void) {
3777
    swap_where_only_one_has_value_and_t_is_not_void(
3778
        rhs, typename std::is_nothrow_move_constructible<T>::type{},
3779
        typename std::is_nothrow_move_constructible<E>::type{});
3780
  }
3781
3782
  void swap_where_only_one_has_value_and_t_is_not_void(
3783
      expected& rhs, t_is_nothrow_move_constructible,
3784
      e_is_nothrow_move_constructible) noexcept {
3785
    auto temp = std::move(val());
3786
    val().~T();
3787
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3788
    rhs.err().~unexpected_type();
3789
    ::new (rhs.valptr()) T(std::move(temp));
3790
    std::swap(this->m_has_val, rhs.m_has_val);
3791
  }
3792
3793
  void swap_where_only_one_has_value_and_t_is_not_void(
3794
      expected& rhs, t_is_nothrow_move_constructible,
3795
      move_constructing_e_can_throw) {
3796
    auto temp = std::move(val());
3797
    val().~T();
3798
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3799
    try {
3800
      ::new (errptr()) unexpected_type(std::move(rhs.err()));
3801
      rhs.err().~unexpected_type();
3802
      ::new (rhs.valptr()) T(std::move(temp));
3803
      std::swap(this->m_has_val, rhs.m_has_val);
3804
    } catch (...) {
3805
      val() = std::move(temp);
3806
      throw;
3807
    }
3808
#else
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
#endif
3814
  }
3815
3816
  void swap_where_only_one_has_value_and_t_is_not_void(
3817
      expected& rhs, move_constructing_t_can_throw,
3818
      e_is_nothrow_move_constructible) {
3819
    auto temp = std::move(rhs.err());
3820
    rhs.err().~unexpected_type();
3821
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3822
    try {
3823
      ::new (rhs.valptr()) T(std::move(val()));
3824
      val().~T();
3825
      ::new (errptr()) unexpected_type(std::move(temp));
3826
      std::swap(this->m_has_val, rhs.m_has_val);
3827
    } catch (...) {
3828
      rhs.err() = std::move(temp);
3829
      throw;
3830
    }
3831
#else
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
#endif
3837
  }
3838
3839
 public:
3840
  template <class OT = T, class OE = E>
3841
  detail::enable_if_t<detail::is_swappable<OT>::value &&
3842
                      detail::is_swappable<OE>::value &&
3843
                      (std::is_nothrow_move_constructible<OT>::value ||
3844
                       std::is_nothrow_move_constructible<OE>::value)>
3845
  swap(expected& rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
3846
                               detail::is_nothrow_swappable<T>::value &&
3847
                               std::is_nothrow_move_constructible<E>::value &&
3848
                               detail::is_nothrow_swappable<E>::value) {
3849
    if (has_value() && rhs.has_value()) {
3850
      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
3851
    } else if (!has_value() && rhs.has_value()) {
3852
      rhs.swap(*this);
3853
    } else if (has_value()) {
3854
      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
3855
    } else {
3856
      using std::swap;
3857
      swap(err(), rhs.err());
3858
    }
3859
  }
3860
3861
  constexpr const T* operator->() const {
3862
    TL_ASSERT(has_value());
3863
    return valptr();
3864
  }
3865
686
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
3866
686
    TL_ASSERT(has_value());
3867
686
    return valptr();
3868
686
  }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator->()
tl::expected<ada::url_aggregator, ada::errors>::operator->()
Line
Count
Source
3865
686
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
3866
686
    TL_ASSERT(has_value());
3867
686
    return valptr();
3868
686
  }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::operator->()
3869
3870
  template <class U = T,
3871
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3872
  constexpr const U& operator*() const& {
3873
    TL_ASSERT(has_value());
3874
    return val();
3875
  }
3876
  template <class U = T,
3877
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3878
0
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
3879
0
    TL_ASSERT(has_value());
3880
0
    return val();
3881
0
  }
Unexecuted instantiation: _ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZNR2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZNR2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEdeIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
3882
  template <class U = T,
3883
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3884
  constexpr const U&& operator*() const&& {
3885
    TL_ASSERT(has_value());
3886
    return std::move(val());
3887
  }
3888
  template <class U = T,
3889
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3890
  TL_EXPECTED_11_CONSTEXPR U&& operator*() && {
3891
    TL_ASSERT(has_value());
3892
    return std::move(val());
3893
  }
3894
3895
686
  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
3895
686
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::has_value() const
3896
4.14k
  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
3896
4.14k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::operator bool() const
3897
3898
  template <class U = T,
3899
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3900
  TL_EXPECTED_11_CONSTEXPR const U& value() const& {
3901
    if (!has_value())
3902
      detail::throw_exception(bad_expected_access<E>(err().value()));
3903
    return val();
3904
  }
3905
  template <class U = T,
3906
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3907
0
  TL_EXPECTED_11_CONSTEXPR U& value() & {
3908
0
    if (!has_value())
3909
0
      detail::throw_exception(bad_expected_access<E>(err().value()));
3910
0
    return val();
3911
0
  }
3912
  template <class U = T,
3913
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3914
  TL_EXPECTED_11_CONSTEXPR const U&& value() const&& {
3915
    if (!has_value())
3916
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
3917
    return std::move(val());
3918
  }
3919
  template <class U = T,
3920
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3921
  TL_EXPECTED_11_CONSTEXPR U&& value() && {
3922
    if (!has_value())
3923
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
3924
    return std::move(val());
3925
  }
3926
3927
  constexpr const E& error() const& {
3928
    TL_ASSERT(!has_value());
3929
    return err().value();
3930
  }
3931
0
  TL_EXPECTED_11_CONSTEXPR E& error() & {
3932
0
    TL_ASSERT(!has_value());
3933
0
    return err().value();
3934
0
  }
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::error() &
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::error() &
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::error() &
3935
  constexpr const E&& error() const&& {
3936
    TL_ASSERT(!has_value());
3937
    return std::move(err().value());
3938
  }
3939
  TL_EXPECTED_11_CONSTEXPR E&& error() && {
3940
    TL_ASSERT(!has_value());
3941
    return std::move(err().value());
3942
  }
3943
3944
  template <class U>
3945
  constexpr T value_or(U&& v) const& {
3946
    static_assert(std::is_copy_constructible<T>::value &&
3947
                      std::is_convertible<U&&, T>::value,
3948
                  "T must be copy-constructible and convertible to from U&&");
3949
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
3950
  }
3951
  template <class U>
3952
  TL_EXPECTED_11_CONSTEXPR T value_or(U&& v) && {
3953
    static_assert(std::is_move_constructible<T>::value &&
3954
                      std::is_convertible<U&&, T>::value,
3955
                  "T must be move-constructible and convertible to from U&&");
3956
    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
3957
  }
3958
};
3959
3960
namespace detail {
3961
template <class Exp>
3962
using exp_t = typename detail::decay_t<Exp>::value_type;
3963
template <class Exp>
3964
using err_t = typename detail::decay_t<Exp>::error_type;
3965
template <class Exp, class Ret>
3966
using ret_t = expected<Ret, err_t<Exp>>;
3967
3968
#ifdef TL_EXPECTED_CXX14
3969
template <class Exp, class F,
3970
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
3971
          class Ret = decltype(detail::invoke(std::declval<F>(),
3972
                                              *std::declval<Exp>()))>
3973
constexpr auto and_then_impl(Exp&& exp, F&& f) {
3974
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3975
3976
  return exp.has_value()
3977
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
3978
             : Ret(unexpect, std::forward<Exp>(exp).error());
3979
}
3980
3981
template <class Exp, class F,
3982
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
3983
          class Ret = decltype(detail::invoke(std::declval<F>()))>
3984
constexpr auto and_then_impl(Exp&& exp, F&& f) {
3985
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3986
3987
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
3988
                         : Ret(unexpect, std::forward<Exp>(exp).error());
3989
}
3990
#else
3991
template <class>
3992
struct TC;
3993
template <class Exp, class F,
3994
          class Ret = decltype(detail::invoke(std::declval<F>(),
3995
                                              *std::declval<Exp>())),
3996
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr>
3997
auto and_then_impl(Exp&& exp, F&& f) -> Ret {
3998
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3999
4000
  return exp.has_value()
4001
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4002
             : Ret(unexpect, std::forward<Exp>(exp).error());
4003
}
4004
4005
template <class Exp, class F,
4006
          class Ret = decltype(detail::invoke(std::declval<F>())),
4007
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr>
4008
constexpr auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4009
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4010
4011
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4012
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4013
}
4014
#endif
4015
4016
#ifdef TL_EXPECTED_CXX14
4017
template <class Exp, class F,
4018
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4019
          class Ret = decltype(detail::invoke(std::declval<F>(),
4020
                                              *std::declval<Exp>())),
4021
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4022
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4023
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4024
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4025
                                                 *std::forward<Exp>(exp)))
4026
                         : result(unexpect, std::forward<Exp>(exp).error());
4027
}
4028
4029
template <class Exp, class F,
4030
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4031
          class Ret = decltype(detail::invoke(std::declval<F>(),
4032
                                              *std::declval<Exp>())),
4033
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4034
auto expected_map_impl(Exp&& exp, F&& f) {
4035
  using result = expected<void, err_t<Exp>>;
4036
  if (exp.has_value()) {
4037
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4038
    return result();
4039
  }
4040
4041
  return result(unexpect, std::forward<Exp>(exp).error());
4042
}
4043
4044
template <class Exp, class F,
4045
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4046
          class Ret = decltype(detail::invoke(std::declval<F>())),
4047
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4048
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4049
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4050
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4051
                         : result(unexpect, std::forward<Exp>(exp).error());
4052
}
4053
4054
template <class Exp, class F,
4055
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4056
          class Ret = decltype(detail::invoke(std::declval<F>())),
4057
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4058
auto expected_map_impl(Exp&& exp, F&& f) {
4059
  using result = expected<void, err_t<Exp>>;
4060
  if (exp.has_value()) {
4061
    detail::invoke(std::forward<F>(f));
4062
    return result();
4063
  }
4064
4065
  return result(unexpect, std::forward<Exp>(exp).error());
4066
}
4067
#else
4068
template <class Exp, class F,
4069
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4070
          class Ret = decltype(detail::invoke(std::declval<F>(),
4071
                                              *std::declval<Exp>())),
4072
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4073
4074
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4075
    -> ret_t<Exp, detail::decay_t<Ret>> {
4076
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4077
4078
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4079
                                                 *std::forward<Exp>(exp)))
4080
                         : result(unexpect, std::forward<Exp>(exp).error());
4081
}
4082
4083
template <class Exp, class F,
4084
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4085
          class Ret = decltype(detail::invoke(std::declval<F>(),
4086
                                              *std::declval<Exp>())),
4087
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4088
4089
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4090
  if (exp.has_value()) {
4091
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4092
    return {};
4093
  }
4094
4095
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4096
}
4097
4098
template <class Exp, class F,
4099
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4100
          class Ret = decltype(detail::invoke(std::declval<F>())),
4101
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4102
4103
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4104
    -> ret_t<Exp, detail::decay_t<Ret>> {
4105
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4106
4107
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4108
                         : result(unexpect, std::forward<Exp>(exp).error());
4109
}
4110
4111
template <class Exp, class F,
4112
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4113
          class Ret = decltype(detail::invoke(std::declval<F>())),
4114
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4115
4116
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4117
  if (exp.has_value()) {
4118
    detail::invoke(std::forward<F>(f));
4119
    return {};
4120
  }
4121
4122
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4123
}
4124
#endif
4125
4126
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4127
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4128
template <class Exp, class F,
4129
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4130
          class Ret = decltype(detail::invoke(std::declval<F>(),
4131
                                              std::declval<Exp>().error())),
4132
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4133
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4134
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4135
  return exp.has_value()
4136
             ? result(*std::forward<Exp>(exp))
4137
             : result(unexpect, detail::invoke(std::forward<F>(f),
4138
                                               std::forward<Exp>(exp).error()));
4139
}
4140
template <class Exp, class F,
4141
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4142
          class Ret = decltype(detail::invoke(std::declval<F>(),
4143
                                              std::declval<Exp>().error())),
4144
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4145
auto map_error_impl(Exp&& exp, F&& f) {
4146
  using result = expected<exp_t<Exp>, monostate>;
4147
  if (exp.has_value()) {
4148
    return result(*std::forward<Exp>(exp));
4149
  }
4150
4151
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4152
  return result(unexpect, monostate{});
4153
}
4154
template <class Exp, class F,
4155
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4156
          class Ret = decltype(detail::invoke(std::declval<F>(),
4157
                                              std::declval<Exp>().error())),
4158
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4159
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4160
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4161
  return exp.has_value()
4162
             ? result()
4163
             : result(unexpect, detail::invoke(std::forward<F>(f),
4164
                                               std::forward<Exp>(exp).error()));
4165
}
4166
template <class Exp, class F,
4167
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4168
          class Ret = decltype(detail::invoke(std::declval<F>(),
4169
                                              std::declval<Exp>().error())),
4170
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4171
auto map_error_impl(Exp&& exp, F&& f) {
4172
  using result = expected<exp_t<Exp>, monostate>;
4173
  if (exp.has_value()) {
4174
    return result();
4175
  }
4176
4177
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4178
  return result(unexpect, monostate{});
4179
}
4180
#else
4181
template <class Exp, class F,
4182
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4183
          class Ret = decltype(detail::invoke(std::declval<F>(),
4184
                                              std::declval<Exp>().error())),
4185
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4186
constexpr auto map_error_impl(Exp&& exp, F&& f)
4187
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4188
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4189
4190
  return exp.has_value()
4191
             ? result(*std::forward<Exp>(exp))
4192
             : result(unexpect, detail::invoke(std::forward<F>(f),
4193
                                               std::forward<Exp>(exp).error()));
4194
}
4195
4196
template <class Exp, class F,
4197
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4198
          class Ret = decltype(detail::invoke(std::declval<F>(),
4199
                                              std::declval<Exp>().error())),
4200
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4201
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4202
  using result = expected<exp_t<Exp>, monostate>;
4203
  if (exp.has_value()) {
4204
    return result(*std::forward<Exp>(exp));
4205
  }
4206
4207
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4208
  return result(unexpect, monostate{});
4209
}
4210
4211
template <class Exp, class F,
4212
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4213
          class Ret = decltype(detail::invoke(std::declval<F>(),
4214
                                              std::declval<Exp>().error())),
4215
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4216
constexpr auto map_error_impl(Exp&& exp, F&& f)
4217
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4218
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4219
4220
  return exp.has_value()
4221
             ? result()
4222
             : result(unexpect, detail::invoke(std::forward<F>(f),
4223
                                               std::forward<Exp>(exp).error()));
4224
}
4225
4226
template <class Exp, class F,
4227
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4228
          class Ret = decltype(detail::invoke(std::declval<F>(),
4229
                                              std::declval<Exp>().error())),
4230
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4231
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4232
  using result = expected<exp_t<Exp>, monostate>;
4233
  if (exp.has_value()) {
4234
    return result();
4235
  }
4236
4237
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4238
  return result(unexpect, monostate{});
4239
}
4240
#endif
4241
4242
#ifdef TL_EXPECTED_CXX14
4243
template <class Exp, class F,
4244
          class Ret = decltype(detail::invoke(std::declval<F>(),
4245
                                              std::declval<Exp>().error())),
4246
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4247
constexpr auto or_else_impl(Exp&& exp, F&& f) {
4248
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4249
  return exp.has_value() ? std::forward<Exp>(exp)
4250
                         : detail::invoke(std::forward<F>(f),
4251
                                          std::forward<Exp>(exp).error());
4252
}
4253
4254
template <class Exp, class F,
4255
          class Ret = decltype(detail::invoke(std::declval<F>(),
4256
                                              std::declval<Exp>().error())),
4257
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4258
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4259
  return exp.has_value() ? std::forward<Exp>(exp)
4260
                         : (detail::invoke(std::forward<F>(f),
4261
                                           std::forward<Exp>(exp).error()),
4262
                            std::forward<Exp>(exp));
4263
}
4264
#else
4265
template <class Exp, class F,
4266
          class Ret = decltype(detail::invoke(std::declval<F>(),
4267
                                              std::declval<Exp>().error())),
4268
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4269
auto or_else_impl(Exp&& exp, F&& f) -> Ret {
4270
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4271
  return exp.has_value() ? std::forward<Exp>(exp)
4272
                         : detail::invoke(std::forward<F>(f),
4273
                                          std::forward<Exp>(exp).error());
4274
}
4275
4276
template <class Exp, class F,
4277
          class Ret = decltype(detail::invoke(std::declval<F>(),
4278
                                              std::declval<Exp>().error())),
4279
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4280
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4281
  return exp.has_value() ? std::forward<Exp>(exp)
4282
                         : (detail::invoke(std::forward<F>(f),
4283
                                           std::forward<Exp>(exp).error()),
4284
                            std::forward<Exp>(exp));
4285
}
4286
#endif
4287
}  // namespace detail
4288
4289
template <class T, class E, class U, class F>
4290
constexpr bool operator==(const expected<T, E>& lhs,
4291
                          const expected<U, F>& rhs) {
4292
  return (lhs.has_value() != rhs.has_value())
4293
             ? false
4294
             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
4295
}
4296
template <class T, class E, class U, class F>
4297
constexpr bool operator!=(const expected<T, E>& lhs,
4298
                          const expected<U, F>& rhs) {
4299
  return (lhs.has_value() != rhs.has_value())
4300
             ? true
4301
             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4302
}
4303
template <class E, class F>
4304
constexpr bool operator==(const expected<void, E>& lhs,
4305
                          const expected<void, F>& rhs) {
4306
  return (lhs.has_value() != rhs.has_value())
4307
             ? false
4308
             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
4309
}
4310
template <class E, class F>
4311
constexpr bool operator!=(const expected<void, E>& lhs,
4312
                          const expected<void, F>& rhs) {
4313
  return (lhs.has_value() != rhs.has_value())
4314
             ? true
4315
             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4316
}
4317
4318
template <class T, class E, class U>
4319
constexpr bool operator==(const expected<T, E>& x, const U& v) {
4320
  return x.has_value() ? *x == v : false;
4321
}
4322
template <class T, class E, class U>
4323
constexpr bool operator==(const U& v, const expected<T, E>& x) {
4324
  return x.has_value() ? *x == v : false;
4325
}
4326
template <class T, class E, class U>
4327
constexpr bool operator!=(const expected<T, E>& x, const U& v) {
4328
  return x.has_value() ? *x != v : true;
4329
}
4330
template <class T, class E, class U>
4331
constexpr bool operator!=(const U& v, const expected<T, E>& x) {
4332
  return x.has_value() ? *x != v : true;
4333
}
4334
4335
template <class T, class E>
4336
constexpr bool operator==(const expected<T, E>& x, const unexpected<E>& e) {
4337
  return x.has_value() ? false : x.error() == e.value();
4338
}
4339
template <class T, class E>
4340
constexpr bool operator==(const unexpected<E>& e, const expected<T, E>& x) {
4341
  return x.has_value() ? false : x.error() == e.value();
4342
}
4343
template <class T, class E>
4344
constexpr bool operator!=(const expected<T, E>& x, const unexpected<E>& e) {
4345
  return x.has_value() ? true : x.error() != e.value();
4346
}
4347
template <class T, class E>
4348
constexpr bool operator!=(const unexpected<E>& e, const expected<T, E>& x) {
4349
  return x.has_value() ? true : x.error() != e.value();
4350
}
4351
4352
template <class T, class E,
4353
          detail::enable_if_t<(std::is_void<T>::value ||
4354
                               std::is_move_constructible<T>::value) &&
4355
                              detail::is_swappable<T>::value &&
4356
                              std::is_move_constructible<E>::value &&
4357
                              detail::is_swappable<E>::value>* = nullptr>
4358
void swap(expected<T, E>& lhs,
4359
          expected<T, E>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
4360
  lhs.swap(rhs);
4361
}
4362
}  // namespace tl
4363
4364
#endif
4365
/* end file include/ada/expected.h */
4366
4367
/* begin file include/ada/url_pattern_regex.h */
4368
/**
4369
 * @file url_search_params.h
4370
 * @brief Declaration for the URL Search Params
4371
 */
4372
#ifndef ADA_URL_PATTERN_REGEX_H
4373
#define ADA_URL_PATTERN_REGEX_H
4374
4375
#include <string>
4376
#include <string_view>
4377
4378
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4379
#include <regex>
4380
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4381
4382
#if ADA_INCLUDE_URL_PATTERN
4383
namespace ada::url_pattern_regex {
4384
4385
template <typename T>
4386
concept regex_concept = requires(T t, std::string_view pattern,
4387
                                 bool ignore_case, std::string_view input) {
4388
  // Ensure the class has a type alias 'regex_type'
4389
  typename T::regex_type;
4390
4391
  // Function to create a regex instance
4392
  {
4393
    T::create_instance(pattern, ignore_case)
4394
  } -> std::same_as<std::optional<typename T::regex_type>>;
4395
4396
  // Function to perform regex search
4397
  {
4398
    T::regex_search(input, std::declval<typename T::regex_type&>())
4399
  } -> std::same_as<std::optional<std::vector<std::optional<std::string>>>>;
4400
4401
  // Function to match regex pattern
4402
  {
4403
    T::regex_match(input, std::declval<typename T::regex_type&>())
4404
  } -> std::same_as<bool>;
4405
4406
  // Copy constructor
4407
  { T(std::declval<const T&>()) } -> std::same_as<T>;
4408
4409
  // Move constructor
4410
  { T(std::declval<T&&>()) } -> std::same_as<T>;
4411
};
4412
4413
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4414
class std_regex_provider final {
4415
 public:
4416
  std_regex_provider() = default;
4417
  using regex_type = std::regex;
4418
  static std::optional<regex_type> create_instance(std::string_view pattern,
4419
                                                   bool ignore_case);
4420
  static std::optional<std::vector<std::optional<std::string>>> regex_search(
4421
      std::string_view input, const regex_type& pattern);
4422
  static bool regex_match(std::string_view input, const regex_type& pattern);
4423
};
4424
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4425
4426
}  // namespace ada::url_pattern_regex
4427
#endif  // ADA_INCLUDE_URL_PATTERN
4428
#endif  // ADA_URL_PATTERN_REGEX_H
4429
/* end file include/ada/url_pattern_regex.h */
4430
/* begin file include/ada/url_pattern_init.h */
4431
/**
4432
 * @file url_pattern_init.h
4433
 * @brief Declaration for the url_pattern_init implementation.
4434
 */
4435
#ifndef ADA_URL_PATTERN_INIT_H
4436
#define ADA_URL_PATTERN_INIT_H
4437
4438
/* begin file include/ada/errors.h */
4439
/**
4440
 * @file errors.h
4441
 * @brief Error type definitions for URL parsing.
4442
 *
4443
 * Defines the error codes that can be returned when URL parsing fails.
4444
 */
4445
#ifndef ADA_ERRORS_H
4446
#define ADA_ERRORS_H
4447
4448
#include <cstdint>
4449
namespace ada {
4450
/**
4451
 * @brief Error codes for URL parsing operations.
4452
 *
4453
 * Used with `tl::expected` to indicate why a URL parsing operation failed.
4454
 */
4455
enum class errors : uint8_t {
4456
  type_error /**< A type error occurred (e.g., invalid URL syntax). */
4457
};
4458
}  // namespace ada
4459
#endif  // ADA_ERRORS_H
4460
/* end file include/ada/errors.h */
4461
4462
#include <string_view>
4463
#include <string>
4464
#include <optional>
4465
#include <iostream>
4466
4467
#if ADA_TESTING
4468
#include <iostream>
4469
#endif  // ADA_TESTING
4470
4471
#if ADA_INCLUDE_URL_PATTERN
4472
namespace ada {
4473
4474
// Important: C++20 allows us to use concept rather than `using` or `typedef
4475
// and allows functions with second argument, which is optional (using either
4476
// std::nullopt or a parameter with default value)
4477
template <typename F>
4478
concept url_pattern_encoding_callback = requires(F f, std::string_view sv) {
4479
  { f(sv) } -> std::same_as<tl::expected<std::string, errors>>;
4480
};
4481
4482
// A structure providing matching patterns for individual components
4483
// of a URL. When a URLPattern is created, or when a URLPattern is
4484
// used to match or test against a URL, the input can be given as
4485
// either a string or a URLPatternInit struct. If a string is given,
4486
// it will be parsed to create a URLPatternInit. The URLPatternInit
4487
// API is defined as part of the URLPattern specification.
4488
// All provided strings must be valid UTF-8.
4489
struct url_pattern_init {
4490
  enum class process_type : uint8_t {
4491
    url,
4492
    pattern,
4493
  };
4494
4495
0
  friend std::ostream& operator<<(std::ostream& os, process_type type) {
4496
0
    switch (type) {
4497
0
      case process_type::url:
4498
0
        return os << "url";
4499
0
      case process_type::pattern:
4500
0
        return os << "pattern";
4501
0
      default:
4502
0
        return os << "unknown";
4503
0
    }
4504
0
  }
4505
4506
  // All strings must be valid UTF-8.
4507
  // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
4508
  static tl::expected<url_pattern_init, errors> process(
4509
      const url_pattern_init& init, process_type type,
4510
      std::optional<std::string_view> protocol = std::nullopt,
4511
      std::optional<std::string_view> username = std::nullopt,
4512
      std::optional<std::string_view> password = std::nullopt,
4513
      std::optional<std::string_view> hostname = std::nullopt,
4514
      std::optional<std::string_view> port = std::nullopt,
4515
      std::optional<std::string_view> pathname = std::nullopt,
4516
      std::optional<std::string_view> search = std::nullopt,
4517
      std::optional<std::string_view> hash = std::nullopt);
4518
4519
  // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init
4520
  static tl::expected<std::string, errors> process_protocol(
4521
      std::string_view value, process_type type);
4522
4523
  // @see https://urlpattern.spec.whatwg.org/#process-username-for-init
4524
  static tl::expected<std::string, errors> process_username(
4525
      std::string_view value, process_type type);
4526
4527
  // @see https://urlpattern.spec.whatwg.org/#process-password-for-init
4528
  static tl::expected<std::string, errors> process_password(
4529
      std::string_view value, process_type type);
4530
4531
  // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init
4532
  static tl::expected<std::string, errors> process_hostname(
4533
      std::string_view value, process_type type);
4534
4535
  // @see https://urlpattern.spec.whatwg.org/#process-port-for-init
4536
  static tl::expected<std::string, errors> process_port(
4537
      std::string_view port, std::string_view protocol, process_type type);
4538
4539
  // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init
4540
  static tl::expected<std::string, errors> process_pathname(
4541
      std::string_view value, std::string_view protocol, process_type type);
4542
4543
  // @see https://urlpattern.spec.whatwg.org/#process-search-for-init
4544
  static tl::expected<std::string, errors> process_search(
4545
      std::string_view value, process_type type);
4546
4547
  // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init
4548
  static tl::expected<std::string, errors> process_hash(std::string_view value,
4549
                                                        process_type type);
4550
4551
#if ADA_TESTING
4552
  friend void PrintTo(const url_pattern_init& init, std::ostream* os) {
4553
    *os << "protocol: '" << init.protocol.value_or("undefined") << "', ";
4554
    *os << "username: '" << init.username.value_or("undefined") << "', ";
4555
    *os << "password: '" << init.password.value_or("undefined") << "', ";
4556
    *os << "hostname: '" << init.hostname.value_or("undefined") << "', ";
4557
    *os << "port: '" << init.port.value_or("undefined") << "', ";
4558
    *os << "pathname: '" << init.pathname.value_or("undefined") << "', ";
4559
    *os << "search: '" << init.search.value_or("undefined") << "', ";
4560
    *os << "hash: '" << init.hash.value_or("undefined") << "', ";
4561
    *os << "base_url: '" << init.base_url.value_or("undefined") << "', ";
4562
  }
4563
#endif  // ADA_TESTING
4564
4565
  bool operator==(const url_pattern_init&) const;
4566
  // If present, must be valid UTF-8.
4567
  std::optional<std::string> protocol{};
4568
  // If present, must be valid UTF-8.
4569
  std::optional<std::string> username{};
4570
  // If present, must be valid UTF-8.
4571
  std::optional<std::string> password{};
4572
  // If present, must be valid UTF-8.
4573
  std::optional<std::string> hostname{};
4574
  // If present, must be valid UTF-8.
4575
  std::optional<std::string> port{};
4576
  // If present, must be valid UTF-8.
4577
  std::optional<std::string> pathname{};
4578
  // If present, must be valid UTF-8.
4579
  std::optional<std::string> search{};
4580
  // If present, must be valid UTF-8.
4581
  std::optional<std::string> hash{};
4582
  // If present, must be valid UTF-8.
4583
  std::optional<std::string> base_url{};
4584
};
4585
}  // namespace ada
4586
#endif  // ADA_INCLUDE_URL_PATTERN
4587
#endif  // ADA_URL_PATTERN_INIT_H
4588
/* end file include/ada/url_pattern_init.h */
4589
4590
/** @private Forward declarations */
4591
namespace ada {
4592
struct url_aggregator;
4593
struct url;
4594
#if ADA_INCLUDE_URL_PATTERN
4595
template <url_pattern_regex::regex_concept regex_provider>
4596
class url_pattern;
4597
struct url_pattern_options;
4598
#endif  // ADA_INCLUDE_URL_PATTERN
4599
enum class errors : uint8_t;
4600
}  // namespace ada
4601
4602
/**
4603
 * @namespace ada::parser
4604
 * @brief Internal URL parsing implementation.
4605
 *
4606
 * Contains the core URL parsing algorithm as specified by the WHATWG URL
4607
 * Standard. These functions are used internally by `ada::parse()`.
4608
 */
4609
namespace ada::parser {
4610
/**
4611
 * Parses a URL string into a URL object.
4612
 *
4613
 * @tparam result_type The type of URL object to create (url or url_aggregator).
4614
 *
4615
 * @param user_input The URL string to parse (must be valid UTF-8).
4616
 * @param base_url Optional base URL for resolving relative URLs.
4617
 *
4618
 * @return The parsed URL object. Check `is_valid` to determine if parsing
4619
 *         succeeded.
4620
 *
4621
 * @see https://url.spec.whatwg.org/#concept-basic-url-parser
4622
 */
4623
template <typename result_type = url_aggregator>
4624
result_type parse_url(std::string_view user_input,
4625
                      const result_type* base_url = nullptr);
4626
4627
extern template url_aggregator parse_url<url_aggregator>(
4628
    std::string_view user_input, const url_aggregator* base_url);
4629
extern template url parse_url<url>(std::string_view user_input,
4630
                                   const url* base_url);
4631
4632
template <typename result_type = url_aggregator, bool store_values = true>
4633
result_type parse_url_impl(std::string_view user_input,
4634
                           const result_type* base_url = nullptr);
4635
4636
extern template url_aggregator parse_url_impl<url_aggregator, true>(
4637
    std::string_view user_input, const url_aggregator* base_url);
4638
extern template url_aggregator parse_url_impl<url_aggregator, false>(
4639
    std::string_view user_input, const url_aggregator* base_url);
4640
extern template url parse_url_impl<url, true>(std::string_view user_input,
4641
                                              const url* base_url);
4642
4643
#if ADA_INCLUDE_URL_PATTERN
4644
template <url_pattern_regex::regex_concept regex_provider>
4645
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
4646
    std::variant<std::string_view, url_pattern_init>&& input,
4647
    const std::string_view* base_url, const url_pattern_options* options);
4648
#endif  // ADA_INCLUDE_URL_PATTERN
4649
4650
}  // namespace ada::parser
4651
4652
#endif  // ADA_PARSER_H
4653
/* end file include/ada/parser.h */
4654
/* begin file include/ada/parser-inl.h */
4655
/**
4656
 * @file parser-inl.h
4657
 */
4658
#ifndef ADA_PARSER_INL_H
4659
#define ADA_PARSER_INL_H
4660
4661
/* begin file include/ada/url_pattern.h */
4662
/**
4663
 * @file url_pattern.h
4664
 * @brief URLPattern API implementation.
4665
 *
4666
 * This header provides the URLPattern API as specified by the WHATWG URL
4667
 * Pattern Standard. URLPattern allows matching URLs against patterns with
4668
 * wildcards and named groups, similar to how regular expressions match strings.
4669
 *
4670
 * @see https://urlpattern.spec.whatwg.org/
4671
 * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
4672
 */
4673
#ifndef ADA_URL_PATTERN_H
4674
#define ADA_URL_PATTERN_H
4675
4676
/* begin file include/ada/implementation.h */
4677
/**
4678
 * @file implementation.h
4679
 * @brief User-facing functions for URL parsing and manipulation.
4680
 *
4681
 * This header provides the primary public API for parsing URLs in Ada.
4682
 * It includes the main `ada::parse()` function which is the recommended
4683
 * entry point for most users.
4684
 *
4685
 * @see https://url.spec.whatwg.org/#api
4686
 */
4687
#ifndef ADA_IMPLEMENTATION_H
4688
#define ADA_IMPLEMENTATION_H
4689
4690
#include <string>
4691
#include <string_view>
4692
#include <optional>
4693
4694
/* begin file include/ada/url.h */
4695
/**
4696
 * @file url.h
4697
 * @brief Declaration for the `ada::url` class.
4698
 *
4699
 * This file contains the `ada::url` struct which represents a parsed URL
4700
 * using separate `std::string` instances for each component. This
4701
 * representation is more flexible but uses more memory than `url_aggregator`.
4702
 *
4703
 * @see url_aggregator.h for a more memory-efficient alternative
4704
 */
4705
#ifndef ADA_URL_H
4706
#define ADA_URL_H
4707
4708
#include <algorithm>
4709
#include <optional>
4710
#include <ostream>
4711
#include <string>
4712
#include <string_view>
4713
4714
/* begin file include/ada/url_components.h */
4715
/**
4716
 * @file url_components.h
4717
 * @brief URL component offset representation for url_aggregator.
4718
 *
4719
 * This file defines the `url_components` struct which stores byte offsets
4720
 * into a URL string buffer. It is used internally by `url_aggregator` to
4721
 * efficiently locate URL components without storing separate strings.
4722
 */
4723
#ifndef ADA_URL_COMPONENTS_H
4724
#define ADA_URL_COMPONENTS_H
4725
4726
namespace ada {
4727
4728
/**
4729
 * @brief Stores byte offsets for URL components within a buffer.
4730
 *
4731
 * The `url_components` struct uses 32-bit offsets to track the boundaries
4732
 * of each URL component within a single string buffer. This enables efficient
4733
 * component extraction without additional memory allocations.
4734
 *
4735
 * Component layout in a URL:
4736
 * ```
4737
 * https://user:pass@example.com:1234/foo/bar?baz#quux
4738
 *       |     |    |          | ^^^^|       |   |
4739
 *       |     |    |          | |   |       |   `----- hash_start
4740
 *       |     |    |          | |   |       `--------- search_start
4741
 *       |     |    |          | |   `----------------- pathname_start
4742
 *       |     |    |          | `--------------------- port
4743
 *       |     |    |          `----------------------- host_end
4744
 *       |     |    `---------------------------------- host_start
4745
 *       |     `--------------------------------------- username_end
4746
 *       `--------------------------------------------- protocol_end
4747
 * ```
4748
 *
4749
 * @note The 32-bit offsets limit URLs to 4GB in length.
4750
 * @note A value of `omitted` (UINT32_MAX) indicates the component is not
4751
 * present.
4752
 */
4753
struct url_components {
4754
  /** Sentinel value indicating a component is not present. */
4755
  constexpr static uint32_t omitted = uint32_t(-1);
4756
4757
4.14k
  url_components() = default;
4758
  url_components(const url_components& u) = default;
4759
  url_components(url_components&& u) noexcept = default;
4760
  url_components& operator=(url_components&& u) noexcept = default;
4761
  url_components& operator=(const url_components& u) = default;
4762
  ~url_components() = default;
4763
4764
  /** Offset of the end of the protocol/scheme (position of ':'). */
4765
  uint32_t protocol_end{0};
4766
4767
  /**
4768
   * Offset of the end of the username.
4769
   * Initialized to 0 (not `omitted`) to simplify username/password getters.
4770
   */
4771
  uint32_t username_end{0};
4772
4773
  /** Offset of the start of the host. */
4774
  uint32_t host_start{0};
4775
4776
  /** Offset of the end of the host. */
4777
  uint32_t host_end{0};
4778
4779
  /** Port number, or `omitted` if no port is specified. */
4780
  uint32_t port{omitted};
4781
4782
  /** Offset of the start of the pathname. */
4783
  uint32_t pathname_start{0};
4784
4785
  /** Offset of the '?' starting the query, or `omitted` if no query. */
4786
  uint32_t search_start{omitted};
4787
4788
  /** Offset of the '#' starting the fragment, or `omitted` if no fragment. */
4789
  uint32_t hash_start{omitted};
4790
4791
  /**
4792
   * Validates that offsets are in ascending order and consistent.
4793
   * Useful for debugging to detect internal corruption.
4794
   * @return `true` if offsets are consistent, `false` otherwise.
4795
   */
4796
  [[nodiscard]] constexpr bool check_offset_consistency() const noexcept;
4797
4798
  /**
4799
   * Returns a JSON string representation of the offsets for debugging.
4800
   * @return A JSON-formatted string with all offset values.
4801
   */
4802
  [[nodiscard]] std::string to_string() const;
4803
4804
};  // struct url_components
4805
}  // namespace ada
4806
#endif
4807
/* end file include/ada/url_components.h */
4808
4809
namespace ada {
4810
4811
struct url_aggregator;
4812
4813
// namespace parser {
4814
// template <typename result_type>
4815
// result_type parse_url(std::string_view user_input,
4816
//                       const result_type* base_url = nullptr);
4817
// template <typename result_type, bool store_values>
4818
// result_type parse_url_impl(std::string_view user_input,
4819
//                            const result_type* base_url = nullptr);
4820
// }
4821
4822
/**
4823
 * @brief Represents a parsed URL with individual string components.
4824
 *
4825
 * The `url` struct stores each URL component (scheme, username, password,
4826
 * host, port, path, query, fragment) as a separate `std::string`. This
4827
 * provides flexibility but incurs more memory allocations compared to
4828
 * `url_aggregator`.
4829
 *
4830
 * **When to use `ada::url`:**
4831
 * - When you need to frequently modify individual URL components
4832
 * - When you want independent ownership of component strings
4833
 *
4834
 * **When to use `ada::url_aggregator` instead:**
4835
 * - For read-mostly operations on parsed URLs
4836
 * - When memory efficiency is important
4837
 * - When you only need string_view access to components
4838
 *
4839
 * @note This type is returned when parsing with `ada::parse<ada::url>()`.
4840
 *       By default, `ada::parse()` returns `ada::url_aggregator`.
4841
 *
4842
 * @see url_aggregator For a more memory-efficient URL representation
4843
 * @see https://url.spec.whatwg.org/#url-representation
4844
 */
4845
struct url : url_base {
4846
0
  url() = default;
4847
0
  url(const url& u) = default;
4848
0
  url(url&& u) noexcept = default;
4849
0
  url& operator=(url&& u) noexcept = default;
4850
0
  url& operator=(const url& u) = default;
4851
0
  ~url() override = default;
4852
4853
  // Fields are ordered so that the most frequently accessed components
4854
  // tend to occupy earlier cache lines and remain close together in memory.
4855
  //
4856
  // Note: The exact object layout (including cache-line boundaries, byte
4857
  // offsets, and member sizes) is implementation- and platform-dependent.
4858
  // This ordering expresses an intent for better cache locality but does not
4859
  // guarantee any specific in-memory layout.
4860
4861
  /**
4862
   * @private
4863
   * A URL's host is null or a host. It is initially null.
4864
   */
4865
  std::optional<std::string> host{};
4866
4867
  /**
4868
   * @private
4869
   * A URL's path is either an ASCII string or a list of zero or more ASCII
4870
   * strings, usually identifying a location.
4871
   */
4872
  std::string path{};
4873
4874
  /**
4875
   * @private
4876
   * A URL's query is either null or an ASCII string. It is initially null.
4877
   */
4878
  std::optional<std::string> query{};
4879
4880
  /**
4881
   * @private
4882
   * A URL's fragment is either null or an ASCII string that can be used for
4883
   * further processing on the resource the URL's other components identify. It
4884
   * is initially null.
4885
   */
4886
  std::optional<std::string> hash{};
4887
4888
  /**
4889
   * @private
4890
   * A URL's port is either null or a 16-bit unsigned integer that identifies a
4891
   * networking port. It is initially null.
4892
   */
4893
  std::optional<uint16_t> port{};
4894
4895
  /**
4896
   * @private
4897
   * A URL's username is an ASCII string identifying a username. It is initially
4898
   * the empty string.
4899
   */
4900
  std::string username{};
4901
4902
  /**
4903
   * @private
4904
   * A URL's password is an ASCII string identifying a password. It is initially
4905
   * the empty string.
4906
   */
4907
  std::string password{};
4908
4909
  /**
4910
   * Checks if the URL has an empty hostname (host is set but empty string).
4911
   * @return `true` if host exists but is empty, `false` otherwise.
4912
   */
4913
  [[nodiscard]] inline bool has_empty_hostname() const noexcept;
4914
4915
  /**
4916
   * Checks if the URL has a non-default port explicitly specified.
4917
   * @return `true` if a port is present, `false` otherwise.
4918
   */
4919
  [[nodiscard]] inline bool has_port() const noexcept;
4920
4921
  /**
4922
   * Checks if the URL has a hostname (including empty hostnames).
4923
   * @return `true` if host is present, `false` otherwise.
4924
   */
4925
  [[nodiscard]] inline bool has_hostname() const noexcept;
4926
4927
  /**
4928
   * Validates whether the hostname is a valid domain according to RFC 1034.
4929
   * Checks that the domain and its labels have valid lengths (max 255 octets
4930
   * total, max 63 octets per label).
4931
   * @return `true` if the domain is valid, `false` otherwise.
4932
   */
4933
  [[nodiscard]] bool has_valid_domain() const noexcept override;
4934
4935
  /**
4936
   * Returns a JSON string representation of this URL for debugging.
4937
   * @return A JSON-formatted string with all URL components.
4938
   */
4939
  [[nodiscard]] std::string to_string() const override;
4940
4941
  /**
4942
   * Returns the full serialized URL (the href).
4943
   * @return The complete URL string (allocates a new string).
4944
   * @see https://url.spec.whatwg.org/#dom-url-href
4945
   */
4946
  [[nodiscard]] ada_really_inline std::string get_href() const;
4947
4948
  /**
4949
   * Returns the byte length of the serialized URL without allocating a string.
4950
   * @return Size of the href in bytes.
4951
   */
4952
  [[nodiscard]] size_t get_href_size() const noexcept;
4953
4954
  /**
4955
   * Returns the URL's origin as a string (scheme + host + port for special
4956
   * URLs).
4957
   * @return A newly allocated string containing the serialized origin.
4958
   * @see https://url.spec.whatwg.org/#concept-url-origin
4959
   */
4960
  [[nodiscard]] std::string get_origin() const override;
4961
4962
  /**
4963
   * Returns the URL's scheme followed by a colon (e.g., "https:").
4964
   * @return A newly allocated string with the protocol.
4965
   * @see https://url.spec.whatwg.org/#dom-url-protocol
4966
   */
4967
  [[nodiscard]] std::string get_protocol() const;
4968
4969
  /**
4970
   * Returns the URL's host and port (e.g., "example.com:8080").
4971
   * If no port is set, returns just the host. Returns empty string if no host.
4972
   * @return A newly allocated string with host:port.
4973
   * @see https://url.spec.whatwg.org/#dom-url-host
4974
   */
4975
  [[nodiscard]] std::string get_host() const;
4976
4977
  /**
4978
   * Returns the URL's hostname (without port).
4979
   * Returns empty string if no host is set.
4980
   * @return A newly allocated string with the hostname.
4981
   * @see https://url.spec.whatwg.org/#dom-url-hostname
4982
   */
4983
  [[nodiscard]] std::string get_hostname() const;
4984
4985
  /**
4986
   * Returns the URL's path component.
4987
   * @return A string_view pointing to the path.
4988
   * @see https://url.spec.whatwg.org/#dom-url-pathname
4989
   */
4990
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept;
4991
4992
  /**
4993
   * Returns the byte length of the pathname without creating a string.
4994
   * @return Size of the pathname in bytes.
4995
   * @see https://url.spec.whatwg.org/#dom-url-pathname
4996
   */
4997
  [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
4998
4999
  /**
5000
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
5001
   * Returns empty string if no query is set.
5002
   * @return A newly allocated string with the search/query.
5003
   * @see https://url.spec.whatwg.org/#dom-url-search
5004
   */
5005
  [[nodiscard]] std::string get_search() const;
5006
5007
  /**
5008
   * Returns the URL's username component.
5009
   * @return A constant reference to the username string.
5010
   * @see https://url.spec.whatwg.org/#dom-url-username
5011
   */
5012
  [[nodiscard]] const std::string& get_username() const noexcept;
5013
5014
  /**
5015
   * Sets the URL's username, percent-encoding special characters.
5016
   * @param input The new username value.
5017
   * @return `true` on success, `false` if the URL cannot have credentials.
5018
   * @see https://url.spec.whatwg.org/#dom-url-username
5019
   */
5020
  bool set_username(std::string_view input);
5021
5022
  /**
5023
   * Sets the URL's password, percent-encoding special characters.
5024
   * @param input The new password value.
5025
   * @return `true` on success, `false` if the URL cannot have credentials.
5026
   * @see https://url.spec.whatwg.org/#dom-url-password
5027
   */
5028
  bool set_password(std::string_view input);
5029
5030
  /**
5031
   * Sets the URL's port from a string (e.g., "8080").
5032
   * @param input The port string. Empty string removes the port.
5033
   * @return `true` on success, `false` if the URL cannot have a port.
5034
   * @see https://url.spec.whatwg.org/#dom-url-port
5035
   */
5036
  bool set_port(std::string_view input);
5037
5038
  /**
5039
   * Sets the URL's fragment/hash (the part after '#').
5040
   * @param input The new hash value (with or without leading '#').
5041
   * @see https://url.spec.whatwg.org/#dom-url-hash
5042
   */
5043
  void set_hash(std::string_view input);
5044
5045
  /**
5046
   * Sets the URL's query string (the part after '?').
5047
   * @param input The new query value (with or without leading '?').
5048
   * @see https://url.spec.whatwg.org/#dom-url-search
5049
   */
5050
  void set_search(std::string_view input);
5051
5052
  /**
5053
   * Sets the URL's pathname.
5054
   * @param input The new path value.
5055
   * @return `true` on success, `false` if the URL has an opaque path.
5056
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5057
   */
5058
  bool set_pathname(std::string_view input);
5059
5060
  /**
5061
   * Sets the URL's host (hostname and optionally port).
5062
   * @param input The new host value (e.g., "example.com:8080").
5063
   * @return `true` on success, `false` if parsing fails.
5064
   * @see https://url.spec.whatwg.org/#dom-url-host
5065
   */
5066
  bool set_host(std::string_view input);
5067
5068
  /**
5069
   * Sets the URL's hostname (without port).
5070
   * @param input The new hostname value.
5071
   * @return `true` on success, `false` if parsing fails.
5072
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5073
   */
5074
  bool set_hostname(std::string_view input);
5075
5076
  /**
5077
   * Sets the URL's protocol/scheme.
5078
   * @param input The new protocol (with or without trailing ':').
5079
   * @return `true` on success, `false` if the scheme is invalid.
5080
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5081
   */
5082
  bool set_protocol(std::string_view input);
5083
5084
  /**
5085
   * Replaces the entire URL by parsing a new href string.
5086
   * @param input The new URL string to parse.
5087
   * @return `true` on success, `false` if parsing fails.
5088
   * @see https://url.spec.whatwg.org/#dom-url-href
5089
   */
5090
  bool set_href(std::string_view input);
5091
5092
  /**
5093
   * Returns the URL's password component.
5094
   * @return A constant reference to the password string.
5095
   * @see https://url.spec.whatwg.org/#dom-url-password
5096
   */
5097
  [[nodiscard]] const std::string& get_password() const noexcept;
5098
5099
  /**
5100
   * Returns the URL's port as a string (e.g., "8080").
5101
   * Returns empty string if no port is set.
5102
   * @return A newly allocated string with the port.
5103
   * @see https://url.spec.whatwg.org/#dom-url-port
5104
   */
5105
  [[nodiscard]] std::string get_port() const;
5106
5107
  /**
5108
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
5109
   * Returns empty string if no fragment is set.
5110
   * @return A newly allocated string with the hash.
5111
   * @see https://url.spec.whatwg.org/#dom-url-hash
5112
   */
5113
  [[nodiscard]] std::string get_hash() const;
5114
5115
  /**
5116
   * Checks if the URL has credentials (non-empty username or password).
5117
   * @return `true` if username or password is non-empty, `false` otherwise.
5118
   */
5119
  [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5120
5121
  /**
5122
   * Returns the URL component offsets for efficient serialization.
5123
   *
5124
   * The components represent byte offsets into the serialized URL:
5125
   * ```
5126
   * https://user:pass@example.com:1234/foo/bar?baz#quux
5127
   *       |     |    |          | ^^^^|       |   |
5128
   *       |     |    |          | |   |       |   `----- hash_start
5129
   *       |     |    |          | |   |       `--------- search_start
5130
   *       |     |    |          | |   `----------------- pathname_start
5131
   *       |     |    |          | `--------------------- port
5132
   *       |     |    |          `----------------------- host_end
5133
   *       |     |    `---------------------------------- host_start
5134
   *       |     `--------------------------------------- username_end
5135
   *       `--------------------------------------------- protocol_end
5136
   * ```
5137
   * @return A newly constructed url_components struct.
5138
   * @see https://github.com/servo/rust-url
5139
   */
5140
  [[nodiscard]] ada_really_inline ada::url_components get_components() const;
5141
5142
  /**
5143
   * Checks if the URL has a fragment/hash component.
5144
   * @return `true` if hash is present, `false` otherwise.
5145
   */
5146
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
5147
5148
  /**
5149
   * Checks if the URL has a query/search component.
5150
   * @return `true` if query is present, `false` otherwise.
5151
   */
5152
  [[nodiscard]] constexpr bool has_search() const noexcept override;
5153
5154
 private:
5155
  friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5156
                                                   const ada::url*);
5157
  friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5158
      std::string_view, const ada::url_aggregator*);
5159
  friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5160
      ada::url& url);
5161
5162
  friend ada::url ada::parser::parse_url_impl<ada::url, true>(std::string_view,
5163
                                                              const ada::url*);
5164
  friend ada::url_aggregator ada::parser::parse_url_impl<
5165
      ada::url_aggregator, true>(std::string_view, const ada::url_aggregator*);
5166
  friend ada::url_aggregator ada::parser::parse_url_impl<
5167
      ada::url_aggregator, false>(std::string_view, const ada::url_aggregator*);
5168
5169
  inline void update_unencoded_base_hash(std::string_view input);
5170
  inline void update_base_hostname(std::string_view input);
5171
  inline void update_base_search(std::string_view input,
5172
                                 const uint8_t query_percent_encode_set[]);
5173
  inline void update_base_search(std::optional<std::string>&& input);
5174
  inline void update_base_pathname(std::string_view input);
5175
  inline void update_base_username(std::string_view input);
5176
  inline void update_base_password(std::string_view input);
5177
  inline void update_base_port(std::optional<uint16_t> input);
5178
5179
  /**
5180
   * Sets the host or hostname according to override condition.
5181
   * Return true on success.
5182
   * @see https://url.spec.whatwg.org/#hostname-state
5183
   */
5184
  template <bool override_hostname = false>
5185
  bool set_host_or_hostname(std::string_view input);
5186
5187
  /**
5188
   * Return true on success.
5189
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5190
   */
5191
  [[nodiscard]] bool parse_ipv4(std::string_view input);
5192
5193
  /**
5194
   * Return true on success.
5195
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5196
   */
5197
  [[nodiscard]] bool parse_ipv6(std::string_view input);
5198
5199
  /**
5200
   * Return true on success.
5201
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5202
   */
5203
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
5204
5205
  /**
5206
   * A URL's scheme is an ASCII string that identifies the type of URL and can
5207
   * be used to dispatch a URL for further processing after parsing. It is
5208
   * initially the empty string. We only set non_special_scheme when the scheme
5209
   * is non-special, otherwise we avoid constructing string.
5210
   *
5211
   * Special schemes are stored in ada::scheme::details::is_special_list so we
5212
   * typically do not need to store them in each url instance.
5213
   */
5214
  std::string non_special_scheme{};
5215
5216
  /**
5217
   * A URL cannot have a username/password/port if its host is null or the empty
5218
   * string, or its scheme is "file".
5219
   */
5220
  [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5221
5222
  ada_really_inline size_t parse_port(
5223
      std::string_view view, bool check_trailing_content) noexcept override;
5224
5225
0
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5226
0
    return this->parse_port(view, false);
5227
0
  }
5228
5229
  /**
5230
   * Parse the host from the provided input. We assume that
5231
   * the input does not contain spaces or tabs. Control
5232
   * characters and spaces are not trimmed (they should have
5233
   * been removed if needed).
5234
   * Return true on success.
5235
   * @see https://url.spec.whatwg.org/#host-parsing
5236
   */
5237
  [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5238
5239
  template <bool has_state_override = false>
5240
  [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5241
5242
  constexpr void clear_pathname() override;
5243
  constexpr void clear_search() override;
5244
  constexpr void set_protocol_as_file();
5245
5246
  /**
5247
   * Parse the path from the provided input.
5248
   * Return true on success. Control characters not
5249
   * trimmed from the ends (they should have
5250
   * been removed if needed).
5251
   *
5252
   * The input is expected to be UTF-8.
5253
   *
5254
   * @see https://url.spec.whatwg.org/
5255
   */
5256
  ada_really_inline void parse_path(std::string_view input);
5257
5258
  /**
5259
   * Set the scheme for this URL. The provided scheme should be a valid
5260
   * scheme string, be lower-cased, not contain spaces or tabs. It should
5261
   * have no spurious trailing or leading content.
5262
   */
5263
  inline void set_scheme(std::string&& new_scheme) noexcept;
5264
5265
  /**
5266
   * Take the scheme from another URL. The scheme string is moved from the
5267
   * provided url.
5268
   */
5269
  constexpr void copy_scheme(ada::url&& u);
5270
5271
  /**
5272
   * Take the scheme from another URL. The scheme string is copied from the
5273
   * provided url.
5274
   */
5275
  constexpr void copy_scheme(const ada::url& u);
5276
5277
};  // struct url
5278
5279
inline std::ostream& operator<<(std::ostream& out, const ada::url& u);
5280
}  // namespace ada
5281
5282
#endif  // ADA_URL_H
5283
/* end file include/ada/url.h */
5284
5285
namespace ada {
5286
5287
/**
5288
 * Result type for URL parsing operations.
5289
 *
5290
 * Uses `tl::expected` to represent either a successfully parsed URL or an
5291
 * error. This allows for exception-free error handling.
5292
 *
5293
 * @tparam result_type The URL type to return (default: `ada::url_aggregator`)
5294
 *
5295
 * @example
5296
 * ```cpp
5297
 * ada::result<ada::url_aggregator> result = ada::parse("https://example.com");
5298
 * if (result) {
5299
 *     // Success: use result.value() or *result
5300
 * } else {
5301
 *     // Error: handle result.error()
5302
 * }
5303
 * ```
5304
 */
5305
template <class result_type = ada::url_aggregator>
5306
using result = tl::expected<result_type, ada::errors>;
5307
5308
/**
5309
 * Parses a URL string according to the WHATWG URL Standard.
5310
 *
5311
 * This is the main entry point for URL parsing in Ada. The function takes
5312
 * a string input and optionally a base URL for resolving relative URLs.
5313
 *
5314
 * @tparam result_type The URL type to return. Can be either `ada::url` or
5315
 *         `ada::url_aggregator` (default). The `url_aggregator` type is more
5316
 *         memory-efficient as it stores components as offsets into a single
5317
 *         buffer.
5318
 *
5319
 * @param input The URL string to parse. Must be valid ASCII or UTF-8 encoded.
5320
 *        Leading and trailing whitespace is automatically trimmed.
5321
 * @param base_url Optional pointer to a base URL for resolving relative URLs.
5322
 *        If nullptr (default), only absolute URLs can be parsed successfully.
5323
 *
5324
 * @return A `result<result_type>` containing either the parsed URL on success,
5325
 *         or an error code on failure. Use the boolean conversion or
5326
 *         `has_value()` to check for success.
5327
 *
5328
 * @note The parser is fully compliant with the WHATWG URL Standard.
5329
 *
5330
 * Parsing fails if the input or the resulting normalized URL exceeds
5331
 * `get_max_input_length()` bytes (default ~4 GB, configurable via
5332
 * `set_max_input_length()`). This accounts for percent-encoding expansion:
5333
 * a short input that normalizes into a long URL is still rejected.
5334
 *
5335
 * @example
5336
 * ```cpp
5337
 * // Parse an absolute URL
5338
 * auto url = ada::parse("https://user:pass@example.com:8080/path?query#hash");
5339
 * if (url) {
5340
 *     std::cout << url->get_hostname(); // "example.com"
5341
 *     std::cout << url->get_pathname(); // "/path"
5342
 * }
5343
 *
5344
 * // Parse a relative URL with a base
5345
 * auto base = ada::parse("https://example.com/dir/");
5346
 * if (base) {
5347
 *     auto relative = ada::parse("../other/page", &*base);
5348
 *     if (relative) {
5349
 *         std::cout << relative->get_href(); //
5350
 * "https://example.com/other/page"
5351
 *     }
5352
 * }
5353
 * ```
5354
 *
5355
 * @see https://url.spec.whatwg.org/#url-parsing
5356
 */
5357
template <class result_type = ada::url_aggregator>
5358
ada_warn_unused ada::result<result_type> parse(
5359
    std::string_view input, const result_type* base_url = nullptr);
5360
5361
extern template ada::result<url> parse<url>(std::string_view input,
5362
                                            const url* base_url);
5363
extern template ada::result<url_aggregator> parse<url_aggregator>(
5364
    std::string_view input, const url_aggregator* base_url);
5365
5366
/**
5367
 * Checks whether a URL string can be successfully parsed.
5368
 *
5369
 * This is a fast validation function that checks if a URL string is valid
5370
 * according to the WHATWG URL Standard without fully constructing a URL
5371
 * object. Use this when you only need to validate URLs without needing
5372
 * their parsed components.
5373
 *
5374
 * When `get_max_input_length()` is set to a value smaller than the default,
5375
 * `can_parse` may still return `true` for overlength inputs that are
5376
 * structurally valid, because the fast path skips the length check for
5377
 * performance. Use `parse()` when strict length enforcement is required.
5378
 *
5379
 * @param input The URL string to validate. Must be valid ASCII or UTF-8.
5380
 * @param base_input Optional pointer to a base URL string for resolving
5381
 *        relative URLs. If nullptr (default), the input is validated as
5382
 *        an absolute URL.
5383
 *
5384
 * @return `true` if the URL can be parsed successfully, `false` otherwise.
5385
 *
5386
 * @example
5387
 * ```cpp
5388
 * // Check absolute URL
5389
 * bool valid = ada::can_parse("https://example.com"); // true
5390
 * bool invalid = ada::can_parse("not a url");         // false
5391
 *
5392
 * // Check relative URL with base
5393
 * std::string_view base = "https://example.com/";
5394
 * bool relative_valid = ada::can_parse("../path", &base); // true
5395
 * ```
5396
 *
5397
 * @see https://url.spec.whatwg.org/#dom-url-canparse
5398
 */
5399
bool can_parse(std::string_view input,
5400
               const std::string_view* base_input = nullptr);
5401
5402
#if ADA_INCLUDE_URL_PATTERN
5403
/**
5404
 * Parses a URL pattern according to the URLPattern specification.
5405
 *
5406
 * URL patterns provide a syntax for matching URLs against patterns, similar
5407
 * to how regular expressions match strings. This is useful for routing and
5408
 * URL-based dispatching.
5409
 *
5410
 * @tparam regex_provider The regex implementation to use for pattern matching.
5411
 *
5412
 * @param input Either a URL pattern string (valid UTF-8) or a URLPatternInit
5413
 *        struct specifying individual component patterns.
5414
 * @param base_url Optional pointer to a base URL string (valid UTF-8) for
5415
 *        resolving relative patterns.
5416
 * @param options Optional pointer to configuration options (e.g., ignore_case).
5417
 *
5418
 * @return A `tl::expected` containing either the parsed url_pattern on success,
5419
 *         or an error code on failure.
5420
 *
5421
 * @see https://urlpattern.spec.whatwg.org
5422
 */
5423
template <url_pattern_regex::regex_concept regex_provider>
5424
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
5425
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
5426
                  const std::string_view* base_url = nullptr,
5427
                  const url_pattern_options* options = nullptr);
5428
#endif  // ADA_INCLUDE_URL_PATTERN
5429
5430
/**
5431
 * Converts a file system path to a file:// URL.
5432
 *
5433
 * Creates a properly formatted file URL from a local file system path.
5434
 * Handles platform-specific path separators and percent-encoding.
5435
 *
5436
 * @param path The file system path to convert. Must be valid ASCII or UTF-8.
5437
 *
5438
 * @return A file:// URL string representing the given path.
5439
 */
5440
std::string href_from_file(std::string_view path);
5441
5442
/**
5443
 * Sets the maximum allowed length for URLs.
5444
 *
5445
 * Both the raw input and the resulting normalized URL (the href) are checked
5446
 * against this limit. Parsing or setter calls that would produce a URL
5447
 * exceeding this length are rejected. The value must fit in a uint32_t.
5448
 * The default is std::numeric_limits<uint32_t>::max() (approximately 4 GB).
5449
 *
5450
 * @param length The new maximum URL length in bytes.
5451
 */
5452
void set_max_input_length(uint32_t length);
5453
5454
/**
5455
 * Returns the current maximum allowed length for URLs.
5456
 *
5457
 * @return The current maximum URL length in bytes.
5458
 */
5459
uint32_t get_max_input_length();
5460
5461
}  // namespace ada
5462
5463
#endif  // ADA_IMPLEMENTATION_H
5464
/* end file include/ada/implementation.h */
5465
5466
#include <ostream>
5467
#include <string>
5468
#include <string_view>
5469
#include <unordered_map>
5470
#include <variant>
5471
#include <vector>
5472
5473
#if ADA_TESTING
5474
#include <iostream>
5475
#endif  // ADA_TESTING
5476
5477
#if ADA_INCLUDE_URL_PATTERN
5478
namespace ada {
5479
5480
enum class url_pattern_part_type : uint8_t {
5481
  // The part represents a simple fixed text string.
5482
  FIXED_TEXT,
5483
  // The part represents a matching group with a custom regular expression.
5484
  REGEXP,
5485
  // The part represents a matching group that matches code points up to the
5486
  // next separator code point. This is typically used for a named group like
5487
  // ":foo" that does not have a custom regular expression.
5488
  SEGMENT_WILDCARD,
5489
  // The part represents a matching group that greedily matches all code points.
5490
  // This is typically used for the "*" wildcard matching group.
5491
  FULL_WILDCARD,
5492
};
5493
5494
// Pattern type for fast-path matching optimization.
5495
// This allows skipping expensive regex evaluation for common simple patterns.
5496
enum class url_pattern_component_type : uint8_t {
5497
  // Pattern is "^$" - only matches empty string
5498
  EMPTY,
5499
  // Pattern is "^<literal>$" - exact string match (no regex needed)
5500
  EXACT_MATCH,
5501
  // Pattern is "^(.*)$" - matches anything (full wildcard)
5502
  FULL_WILDCARD,
5503
  // Pattern requires actual regex evaluation
5504
  REGEXP,
5505
};
5506
5507
enum class url_pattern_part_modifier : uint8_t {
5508
  // The part does not have a modifier.
5509
  none,
5510
  // The part has an optional modifier indicated by the U+003F (?) code point.
5511
  optional,
5512
  // The part has a "zero or more" modifier indicated by the U+002A (*) code
5513
  // point.
5514
  zero_or_more,
5515
  // The part has a "one or more" modifier indicated by the U+002B (+) code
5516
  // point.
5517
  one_or_more,
5518
};
5519
5520
// @see https://urlpattern.spec.whatwg.org/#part
5521
class url_pattern_part {
5522
 public:
5523
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5524
                   url_pattern_part_modifier _modifier)
5525
0
      : type(_type), value(std::move(_value)), modifier(_modifier) {}
5526
5527
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5528
                   url_pattern_part_modifier _modifier, std::string&& _name,
5529
                   std::string&& _prefix, std::string&& _suffix)
5530
      : type(_type),
5531
        value(std::move(_value)),
5532
        modifier(_modifier),
5533
        name(std::move(_name)),
5534
        prefix(std::move(_prefix)),
5535
0
        suffix(std::move(_suffix)) {}
5536
  // A part has an associated type, a string, which must be set upon creation.
5537
  url_pattern_part_type type;
5538
  // A part has an associated value, a string, which must be set upon creation.
5539
  std::string value;
5540
  // A part has an associated modifier a string, which must be set upon
5541
  // creation.
5542
  url_pattern_part_modifier modifier;
5543
  // A part has an associated name, a string, initially the empty string.
5544
  std::string name{};
5545
  // A part has an associated prefix, a string, initially the empty string.
5546
  std::string prefix{};
5547
  // A part has an associated suffix, a string, initially the empty string.
5548
  std::string suffix{};
5549
5550
  inline bool is_regexp() const noexcept;
5551
};
5552
5553
// @see https://urlpattern.spec.whatwg.org/#options-header
5554
struct url_pattern_compile_component_options {
5555
  url_pattern_compile_component_options() = default;
5556
  explicit url_pattern_compile_component_options(
5557
      std::optional<char> new_delimiter = std::nullopt,
5558
      std::optional<char> new_prefix = std::nullopt) noexcept
5559
6
      : delimiter(new_delimiter), prefix(new_prefix) {}
5560
5561
  inline std::string_view get_delimiter() const ada_warn_unused;
5562
  inline std::string_view get_prefix() const ada_warn_unused;
5563
5564
  // @see https://urlpattern.spec.whatwg.org/#options-ignore-case
5565
  bool ignore_case = false;
5566
5567
  static url_pattern_compile_component_options DEFAULT;
5568
  static url_pattern_compile_component_options HOSTNAME;
5569
  static url_pattern_compile_component_options PATHNAME;
5570
5571
 private:
5572
  // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point
5573
  std::optional<char> delimiter{};
5574
  // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point
5575
  std::optional<char> prefix{};
5576
};
5577
5578
// The default options is an options struct with delimiter code point set to
5579
// the empty string and prefix code point set to the empty string.
5580
inline url_pattern_compile_component_options
5581
    url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt);
5582
5583
// The hostname options is an options struct with delimiter code point set
5584
// "." and prefix code point set to the empty string.
5585
inline url_pattern_compile_component_options
5586
    url_pattern_compile_component_options::HOSTNAME('.', std::nullopt);
5587
5588
// The pathname options is an options struct with delimiter code point set
5589
// "/" and prefix code point set to "/".
5590
inline url_pattern_compile_component_options
5591
    url_pattern_compile_component_options::PATHNAME('/', '/');
5592
5593
// A struct providing the URLPattern matching results for a single
5594
// URL component. The URLPatternComponentResult is only ever used
5595
// as a member attribute of a URLPatternResult struct. The
5596
// URLPatternComponentResult API is defined as part of the URLPattern
5597
// specification.
5598
struct url_pattern_component_result {
5599
  std::string input;
5600
  std::unordered_map<std::string, std::optional<std::string>> groups;
5601
5602
  bool operator==(const url_pattern_component_result&) const;
5603
5604
#if ADA_TESTING
5605
  friend void PrintTo(const url_pattern_component_result& result,
5606
                      std::ostream* os) {
5607
    *os << "input: '" << result.input << "', group: ";
5608
    for (const auto& group : result.groups) {
5609
      *os << "(" << group.first << ", " << group.second.value_or("undefined")
5610
          << ") ";
5611
    }
5612
  }
5613
#endif  // ADA_TESTING
5614
};
5615
5616
template <url_pattern_regex::regex_concept regex_provider>
5617
class url_pattern_component {
5618
 public:
5619
  url_pattern_component() = default;
5620
5621
  // This function explicitly takes a std::string because it is moved.
5622
  // To avoid unnecessary copy, move each value while calling the constructor.
5623
  url_pattern_component(std::string&& new_pattern,
5624
                        typename regex_provider::regex_type&& new_regexp,
5625
                        std::vector<std::string>&& new_group_name_list,
5626
                        bool new_has_regexp_groups,
5627
                        url_pattern_component_type new_type,
5628
                        std::string&& new_exact_match_value = {})
5629
      : regexp(std::move(new_regexp)),
5630
        pattern(std::move(new_pattern)),
5631
        group_name_list(std::move(new_group_name_list)),
5632
        exact_match_value(std::move(new_exact_match_value)),
5633
        has_regexp_groups(new_has_regexp_groups),
5634
        type(new_type) {}
5635
5636
  // @see https://urlpattern.spec.whatwg.org/#compile-a-component
5637
  template <url_pattern_encoding_callback F>
5638
  static tl::expected<url_pattern_component, errors> compile(
5639
      std::string_view input, F& encoding_callback,
5640
      url_pattern_compile_component_options& options);
5641
5642
  // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result
5643
  url_pattern_component_result create_component_match_result(
5644
      std::string&& input,
5645
      std::vector<std::optional<std::string>>&& exec_result);
5646
5647
  // Fast path test that returns true/false without constructing result groups.
5648
  // Uses cached pattern type to skip regex evaluation for simple patterns.
5649
  bool fast_test(std::string_view input) const noexcept;
5650
5651
  // Fast path match that returns capture groups without regex for simple
5652
  // patterns. Returns nullopt if pattern doesn't match, otherwise returns
5653
  // capture groups.
5654
  std::optional<std::vector<std::optional<std::string>>> fast_match(
5655
      std::string_view input) const;
5656
5657
#if ADA_TESTING
5658
  friend void PrintTo(const url_pattern_component& component,
5659
                      std::ostream* os) {
5660
    *os << "pattern: '" << component.pattern
5661
        << "', has_regexp_groups: " << component.has_regexp_groups
5662
        << "group_name_list: ";
5663
    for (const auto& name : component.group_name_list) {
5664
      *os << name << ", ";
5665
    }
5666
  }
5667
#endif  // ADA_TESTING
5668
5669
  typename regex_provider::regex_type regexp{};
5670
  std::string pattern{};
5671
  std::vector<std::string> group_name_list{};
5672
  // For EXACT_MATCH type: the literal string to compare against
5673
  std::string exact_match_value{};
5674
  bool has_regexp_groups = false;
5675
  // Cached pattern type for fast-path optimization
5676
  url_pattern_component_type type = url_pattern_component_type::REGEXP;
5677
};
5678
5679
// A URLPattern input can be either a string or a URLPatternInit object.
5680
// If it is a string, it must be a valid UTF-8 string.
5681
using url_pattern_input = std::variant<std::string_view, url_pattern_init>;
5682
5683
// A struct providing the URLPattern matching results for all
5684
// components of a URL. The URLPatternResult API is defined as
5685
// part of the URLPattern specification.
5686
struct url_pattern_result {
5687
  std::vector<url_pattern_input> inputs;
5688
  url_pattern_component_result protocol;
5689
  url_pattern_component_result username;
5690
  url_pattern_component_result password;
5691
  url_pattern_component_result hostname;
5692
  url_pattern_component_result port;
5693
  url_pattern_component_result pathname;
5694
  url_pattern_component_result search;
5695
  url_pattern_component_result hash;
5696
};
5697
5698
struct url_pattern_options {
5699
  bool ignore_case = false;
5700
5701
#if ADA_TESTING
5702
  friend void PrintTo(const url_pattern_options& options, std::ostream* os) {
5703
    *os << "ignore_case: '" << options.ignore_case;
5704
  }
5705
#endif  // ADA_TESTING
5706
};
5707
5708
/**
5709
 * @brief URL pattern matching class implementing the URLPattern API.
5710
 *
5711
 * URLPattern provides a way to match URLs against patterns with wildcards
5712
 * and named capture groups. It's useful for routing, URL-based dispatching,
5713
 * and URL validation.
5714
 *
5715
 * Pattern syntax supports:
5716
 * - Literal text matching
5717
 * - Named groups: `:name` (matches up to the next separator)
5718
 * - Wildcards: `*` (matches everything)
5719
 * - Custom regex: `(pattern)`
5720
 * - Optional segments: `:name?`
5721
 * - Repeated segments: `:name+`, `:name*`
5722
 *
5723
 * @tparam regex_provider The regex implementation to use for pattern matching.
5724
 *         Must satisfy the url_pattern_regex::regex_concept.
5725
 *
5726
 * @note All string inputs must be valid UTF-8.
5727
 *
5728
 * @see https://urlpattern.spec.whatwg.org/
5729
 */
5730
template <url_pattern_regex::regex_concept regex_provider>
5731
class url_pattern {
5732
 public:
5733
  url_pattern() = default;
5734
5735
  /**
5736
   * If non-null, base_url must pointer at a valid UTF-8 string.
5737
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec
5738
   */
5739
  result<std::optional<url_pattern_result>> exec(
5740
      const url_pattern_input& input,
5741
      const std::string_view* base_url = nullptr);
5742
5743
  /**
5744
   * If non-null, base_url must pointer at a valid UTF-8 string.
5745
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
5746
   */
5747
  result<bool> test(const url_pattern_input& input,
5748
                    const std::string_view* base_url = nullptr);
5749
5750
  /**
5751
   * @see https://urlpattern.spec.whatwg.org/#url-pattern-match
5752
   * This function expects a valid UTF-8 string if input is a string.
5753
   */
5754
  result<std::optional<url_pattern_result>> match(
5755
      const url_pattern_input& input,
5756
      const std::string_view* base_url_string = nullptr);
5757
5758
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol
5759
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
5760
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username
5761
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
5762
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password
5763
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
5764
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname
5765
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
5766
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port
5767
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
5768
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname
5769
  [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound;
5770
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search
5771
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
5772
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash
5773
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
5774
5775
  // If ignoreCase is true, the JavaScript regular expression created for each
5776
  // pattern must use the `vi` flag. Otherwise, they must use the `v` flag.
5777
  [[nodiscard]] bool ignore_case() const;
5778
5779
  // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups
5780
  [[nodiscard]] bool has_regexp_groups() const;
5781
5782
  // Helper to test all components at once. Returns true if all match.
5783
  [[nodiscard]] bool test_components(
5784
      std::string_view protocol, std::string_view username,
5785
      std::string_view password, std::string_view hostname,
5786
      std::string_view port, std::string_view pathname, std::string_view search,
5787
      std::string_view hash) const;
5788
5789
#if ADA_TESTING
5790
  friend void PrintTo(const url_pattern& c, std::ostream* os) {
5791
    *os << "protocol_component: '" << c.get_protocol() << ", ";
5792
    *os << "username_component: '" << c.get_username() << ", ";
5793
    *os << "password_component: '" << c.get_password() << ", ";
5794
    *os << "hostname_component: '" << c.get_hostname() << ", ";
5795
    *os << "port_component: '" << c.get_port() << ", ";
5796
    *os << "pathname_component: '" << c.get_pathname() << ", ";
5797
    *os << "search_component: '" << c.get_search() << ", ";
5798
    *os << "hash_component: '" << c.get_hash();
5799
  }
5800
#endif  // ADA_TESTING
5801
5802
  template <url_pattern_regex::regex_concept P>
5803
  friend tl::expected<url_pattern<P>, errors> parser::parse_url_pattern_impl(
5804
      std::variant<std::string_view, url_pattern_init>&& input,
5805
      const std::string_view* base_url, const url_pattern_options* options);
5806
5807
  /**
5808
   * @private
5809
   * We can not make this private due to a LLVM bug.
5810
   * Ref: https://github.com/ada-url/ada/pull/859
5811
   */
5812
  url_pattern_component<regex_provider> protocol_component{};
5813
  /**
5814
   * @private
5815
   * We can not make this private due to a LLVM bug.
5816
   * Ref: https://github.com/ada-url/ada/pull/859
5817
   */
5818
  url_pattern_component<regex_provider> username_component{};
5819
  /**
5820
   * @private
5821
   * We can not make this private due to a LLVM bug.
5822
   * Ref: https://github.com/ada-url/ada/pull/859
5823
   */
5824
  url_pattern_component<regex_provider> password_component{};
5825
  /**
5826
   * @private
5827
   * We can not make this private due to a LLVM bug.
5828
   * Ref: https://github.com/ada-url/ada/pull/859
5829
   */
5830
  url_pattern_component<regex_provider> hostname_component{};
5831
  /**
5832
   * @private
5833
   * We can not make this private due to a LLVM bug.
5834
   * Ref: https://github.com/ada-url/ada/pull/859
5835
   */
5836
  url_pattern_component<regex_provider> port_component{};
5837
  /**
5838
   * @private
5839
   * We can not make this private due to a LLVM bug.
5840
   * Ref: https://github.com/ada-url/ada/pull/859
5841
   */
5842
  url_pattern_component<regex_provider> pathname_component{};
5843
  /**
5844
   * @private
5845
   * We can not make this private due to a LLVM bug.
5846
   * Ref: https://github.com/ada-url/ada/pull/859
5847
   */
5848
  url_pattern_component<regex_provider> search_component{};
5849
  /**
5850
   * @private
5851
   * We can not make this private due to a LLVM bug.
5852
   * Ref: https://github.com/ada-url/ada/pull/859
5853
   */
5854
  url_pattern_component<regex_provider> hash_component{};
5855
  /**
5856
   * @private
5857
   * We can not make this private due to a LLVM bug.
5858
   * Ref: https://github.com/ada-url/ada/pull/859
5859
   */
5860
  bool ignore_case_ = false;
5861
};
5862
}  // namespace ada
5863
#endif  // ADA_INCLUDE_URL_PATTERN
5864
#endif
5865
/* end file include/ada/url_pattern.h */
5866
/* begin file include/ada/url_pattern_helpers.h */
5867
/**
5868
 * @file url_pattern_helpers.h
5869
 * @brief Declaration for the URLPattern helpers.
5870
 */
5871
#ifndef ADA_URL_PATTERN_HELPERS_H
5872
#define ADA_URL_PATTERN_HELPERS_H
5873
5874
5875
#include <string>
5876
#include <tuple>
5877
#include <vector>
5878
5879
#if ADA_INCLUDE_URL_PATTERN
5880
namespace ada {
5881
enum class errors : uint8_t;
5882
}
5883
5884
namespace ada::url_pattern_helpers {
5885
5886
// @see https://urlpattern.spec.whatwg.org/#token
5887
enum class token_type : uint8_t {
5888
  INVALID_CHAR,    // 0
5889
  OPEN,            // 1
5890
  CLOSE,           // 2
5891
  REGEXP,          // 3
5892
  NAME,            // 4
5893
  CHAR,            // 5
5894
  ESCAPED_CHAR,    // 6
5895
  OTHER_MODIFIER,  // 7
5896
  ASTERISK,        // 8
5897
  END,             // 9
5898
};
5899
5900
#ifdef ADA_TESTING
5901
std::string to_string(token_type type);
5902
#endif  // ADA_TESTING
5903
5904
// @see https://urlpattern.spec.whatwg.org/#tokenize-policy
5905
enum class token_policy {
5906
  strict,
5907
  lenient,
5908
};
5909
5910
// @see https://urlpattern.spec.whatwg.org/#tokens
5911
class token {
5912
 public:
5913
  token(token_type _type, size_t _index, std::string_view _value)
5914
0
      : type(_type), index(_index), value(_value) {}
5915
5916
  // A token has an associated type, a string, initially "invalid-char".
5917
  token_type type = token_type::INVALID_CHAR;
5918
5919
  // A token has an associated index, a number, initially 0. It is the position
5920
  // of the first code point in the pattern string represented by the token.
5921
  size_t index = 0;
5922
5923
  // A token has an associated value, a string, initially the empty string. It
5924
  // contains the code points from the pattern string represented by the token.
5925
  std::string_view value{};
5926
};
5927
5928
// @see https://urlpattern.spec.whatwg.org/#pattern-parser
5929
template <url_pattern_encoding_callback F>
5930
class url_pattern_parser {
5931
 public:
5932
  url_pattern_parser(F& encoding_callback_,
5933
                     std::string_view segment_wildcard_regexp_)
5934
      : encoding_callback(encoding_callback_),
5935
        segment_wildcard_regexp(segment_wildcard_regexp_) {}
5936
5937
  bool can_continue() const { return index < tokens.size(); }
5938
5939
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token
5940
  token* try_consume_token(token_type type);
5941
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token
5942
  token* try_consume_modifier_token();
5943
  // @see
5944
  // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token
5945
  token* try_consume_regexp_or_wildcard_token(const token* name_token);
5946
  // @see https://urlpattern.spec.whatwg.org/#consume-text
5947
  std::string consume_text();
5948
  // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token
5949
  bool consume_required_token(token_type type);
5950
  // @see
5951
  // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value
5952
  std::optional<errors> maybe_add_part_from_the_pending_fixed_value()
5953
      ada_warn_unused;
5954
  // @see https://urlpattern.spec.whatwg.org/#add-a-part
5955
  std::optional<errors> add_part(std::string_view prefix, token* name_token,
5956
                                 token* regexp_or_wildcard_token,
5957
                                 std::string_view suyffix,
5958
                                 token* modifier_token) ada_warn_unused;
5959
5960
  std::vector<token> tokens{};
5961
  F& encoding_callback;
5962
  std::string segment_wildcard_regexp;
5963
  std::vector<url_pattern_part> parts{};
5964
  std::string pending_fixed_value{};
5965
  size_t index = 0;
5966
  size_t next_numeric_name = 0;
5967
};
5968
5969
// @see https://urlpattern.spec.whatwg.org/#tokenizer
5970
class Tokenizer {
5971
 public:
5972
  explicit Tokenizer(std::string_view new_input, token_policy new_policy)
5973
0
      : input(new_input), policy(new_policy) {}
5974
5975
  // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point
5976
  constexpr void get_next_code_point();
5977
5978
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
5979
  constexpr void seek_and_get_next_code_point(size_t index);
5980
5981
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
5982
5983
  void add_token(token_type type, size_t next_position, size_t value_position,
5984
                 size_t value_length);
5985
5986
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
5987
  void add_token_with_default_length(token_type type, size_t next_position,
5988
                                     size_t value_position);
5989
5990
  // @see
5991
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
5992
  void add_token_with_defaults(token_type type);
5993
5994
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
5995
  std::optional<errors> process_tokenizing_error(
5996
      size_t next_position, size_t value_position) ada_warn_unused;
5997
5998
  friend tl::expected<std::vector<token>, errors> tokenize(
5999
      std::string_view input, token_policy policy);
6000
6001
 private:
6002
  // has an associated input, a pattern string, initially the empty string.
6003
  std::string_view input;
6004
  // has an associated policy, a tokenize policy, initially "strict".
6005
  token_policy policy;
6006
  // has an associated token list, a token list, initially an empty list.
6007
  std::vector<token> token_list{};
6008
  // has an associated index, a number, initially 0.
6009
  size_t index = 0;
6010
  // has an associated next index, a number, initially 0.
6011
  size_t next_index = 0;
6012
  // has an associated code point, a Unicode code point, initially null.
6013
  char32_t code_point{};
6014
};
6015
6016
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
6017
template <url_pattern_regex::regex_concept regex_provider>
6018
struct constructor_string_parser {
6019
  explicit constructor_string_parser(std::string_view new_input,
6020
                                     std::vector<token>&& new_token_list)
6021
      : input(new_input), token_list(std::move(new_token_list)) {}
6022
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
6023
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
6024
6025
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
6026
  enum class State {
6027
    INIT,
6028
    PROTOCOL,
6029
    AUTHORITY,
6030
    USERNAME,
6031
    PASSWORD,
6032
    HOSTNAME,
6033
    PORT,
6034
    PATHNAME,
6035
    SEARCH,
6036
    HASH,
6037
    DONE,
6038
  };
6039
6040
  // @see
6041
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6042
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6043
6044
 private:
6045
  // @see https://urlpattern.spec.whatwg.org/#rewind
6046
  constexpr void rewind();
6047
6048
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6049
  constexpr bool is_hash_prefix();
6050
6051
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6052
  constexpr bool is_search_prefix();
6053
6054
  // @see https://urlpattern.spec.whatwg.org/#change-state
6055
  void change_state(State state, size_t skip);
6056
6057
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6058
  constexpr bool is_group_open() const;
6059
6060
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6061
  constexpr bool is_group_close() const;
6062
6063
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6064
  constexpr bool is_protocol_suffix() const;
6065
6066
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6067
  constexpr bool next_is_authority_slashes() const;
6068
6069
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6070
  constexpr bool is_an_identity_terminator() const;
6071
6072
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6073
  constexpr bool is_pathname_start() const;
6074
6075
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6076
  constexpr bool is_password_prefix() const;
6077
6078
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6079
  constexpr bool is_an_ipv6_open() const;
6080
6081
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6082
  constexpr bool is_an_ipv6_close() const;
6083
6084
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6085
  constexpr bool is_port_prefix() const;
6086
6087
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6088
  constexpr bool is_non_special_pattern_char(size_t index,
6089
                                             uint32_t value) const;
6090
6091
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6092
  constexpr const token* get_safe_token(size_t index) const;
6093
6094
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6095
  std::string make_component_string();
6096
  // has an associated input, a string, which must be set upon creation.
6097
  std::string_view input;
6098
  // has an associated token list, a token list, which must be set upon
6099
  // creation.
6100
  std::vector<token> token_list;
6101
  // has an associated result, a URLPatternInit, initially set to a new
6102
  // URLPatternInit.
6103
  url_pattern_init result{};
6104
  // has an associated component start, a number, initially set to 0.
6105
  size_t component_start = 0;
6106
  // has an associated token index, a number, initially set to 0.
6107
  size_t token_index = 0;
6108
  // has an associated token increment, a number, initially set to 1.
6109
  size_t token_increment = 1;
6110
  // has an associated group depth, a number, initially set to 0.
6111
  size_t group_depth = 0;
6112
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6113
  // 0.
6114
  size_t hostname_ipv6_bracket_depth = 0;
6115
  // has an associated protocol matches a special scheme flag, a boolean,
6116
  // initially set to false.
6117
  bool protocol_matches_a_special_scheme_flag = false;
6118
  // has an associated state, a string, initially set to "init".
6119
  State state = State::INIT;
6120
};
6121
6122
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6123
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6124
6125
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6126
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6127
6128
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6129
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6130
6131
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6132
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6133
6134
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6135
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6136
    std::string_view input);
6137
6138
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6139
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6140
6141
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6142
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6143
    std::string_view input, std::string_view protocol);
6144
6145
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6146
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6147
6148
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6149
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6150
    std::string_view input);
6151
6152
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6153
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6154
6155
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6156
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6157
6158
// @see https://urlpattern.spec.whatwg.org/#tokenize
6159
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6160
                                                  token_policy policy);
6161
6162
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6163
std::string process_base_url_string(std::string_view input,
6164
                                    url_pattern_init::process_type type);
6165
6166
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6167
std::string escape_pattern_string(std::string_view input);
6168
6169
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6170
std::string escape_regexp_string(std::string_view input);
6171
6172
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6173
constexpr bool is_absolute_pathname(
6174
    std::string_view input, url_pattern_init::process_type type) noexcept;
6175
6176
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6177
template <url_pattern_encoding_callback F>
6178
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6179
    std::string_view input, url_pattern_compile_component_options& options,
6180
    F& encoding_callback);
6181
6182
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6183
std::string generate_pattern_string(
6184
    std::vector<url_pattern_part>& part_list,
6185
    url_pattern_compile_component_options& options);
6186
6187
// @see
6188
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6189
std::tuple<std::string, std::vector<std::string>>
6190
generate_regular_expression_and_name_list(
6191
    const std::vector<url_pattern_part>& part_list,
6192
    url_pattern_compile_component_options options);
6193
6194
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6195
bool is_ipv6_address(std::string_view input) noexcept;
6196
6197
// @see
6198
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6199
template <url_pattern_regex::regex_concept regex_provider>
6200
bool protocol_component_matches_special_scheme(
6201
    ada::url_pattern_component<regex_provider>& input);
6202
6203
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6204
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6205
6206
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6207
std::string generate_segment_wildcard_regexp(
6208
    url_pattern_compile_component_options options);
6209
6210
}  // namespace ada::url_pattern_helpers
6211
#endif  // ADA_INCLUDE_URL_PATTERN
6212
#endif
6213
/* end file include/ada/url_pattern_helpers.h */
6214
6215
#include <string>
6216
#include <string_view>
6217
#include <variant>
6218
6219
namespace ada::parser {
6220
#if ADA_INCLUDE_URL_PATTERN
6221
template <url_pattern_regex::regex_concept regex_provider>
6222
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6223
    std::variant<std::string_view, url_pattern_init>&& input,
6224
    const std::string_view* base_url, const url_pattern_options* options) {
6225
  // Let init be null.
6226
  url_pattern_init init;
6227
6228
  // If input is a scalar value string then:
6229
  if (std::holds_alternative<std::string_view>(input)) {
6230
    // Set init to the result of running parse a constructor string given input.
6231
    auto parse_result =
6232
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6233
            std::get<std::string_view>(input));
6234
    if (!parse_result) {
6235
      ada_log("constructor_string_parser::parse failed");
6236
      return tl::unexpected(parse_result.error());
6237
    }
6238
    init = std::move(*parse_result);
6239
    // If baseURL is null and init["protocol"] does not exist, then throw a
6240
    // TypeError.
6241
    if (!base_url && !init.protocol) {
6242
      ada_log("base url is null and protocol is not set");
6243
      return tl::unexpected(errors::type_error);
6244
    }
6245
6246
    // If baseURL is not null, set init["baseURL"] to baseURL.
6247
    if (base_url) {
6248
      init.base_url = std::string(*base_url);
6249
    }
6250
  } else {
6251
    // Assert: input is a URLPatternInit.
6252
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6253
    // If baseURL is not null, then throw a TypeError.
6254
    if (base_url) {
6255
      ada_log("base url is not null");
6256
      return tl::unexpected(errors::type_error);
6257
    }
6258
    // Optimization: Avoid copy by moving the input value.
6259
    // Set init to input.
6260
    init = std::move(std::get<url_pattern_init>(input));
6261
  }
6262
6263
  // Let processedInit be the result of process a URLPatternInit given init,
6264
  // "pattern", null, null, null, null, null, null, null, and null.
6265
  auto processed_init =
6266
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6267
  if (!processed_init) {
6268
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6269
    return tl::unexpected(processed_init.error());
6270
  }
6271
6272
  // For each componentName of  "protocol", "username", "password", "hostname",
6273
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6274
  // not exist, then set processedInit[componentName] to "*".
6275
  ADA_ASSERT_TRUE(processed_init.has_value());
6276
  if (!processed_init->protocol) processed_init->protocol = "*";
6277
  if (!processed_init->username) processed_init->username = "*";
6278
  if (!processed_init->password) processed_init->password = "*";
6279
  if (!processed_init->hostname) processed_init->hostname = "*";
6280
  if (!processed_init->port) processed_init->port = "*";
6281
  if (!processed_init->pathname) processed_init->pathname = "*";
6282
  if (!processed_init->search) processed_init->search = "*";
6283
  if (!processed_init->hash) processed_init->hash = "*";
6284
6285
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6286
  ada_log("-- processed_init->username: ", processed_init->username.value());
6287
  ada_log("-- processed_init->password: ", processed_init->password.value());
6288
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6289
  ada_log("-- processed_init->port: ", processed_init->port.value());
6290
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6291
  ada_log("-- processed_init->search: ", processed_init->search.value());
6292
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6293
6294
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6295
  // is a string which represents its corresponding default port in radix-10
6296
  // using ASCII digits then set processedInit["port"] to the empty string.
6297
  // TODO: Optimization opportunity.
6298
  if (scheme::is_special(*processed_init->protocol)) {
6299
    std::string_view port = processed_init->port.value();
6300
    if (std::to_string(scheme::get_special_port(*processed_init->protocol)) ==
6301
        port) {
6302
      processed_init->port->clear();
6303
    }
6304
  }
6305
6306
  // Let urlPattern be a new URL pattern.
6307
  url_pattern<regex_provider> url_pattern_{};
6308
6309
  // Set urlPattern's protocol component to the result of compiling a component
6310
  // given processedInit["protocol"], canonicalize a protocol, and default
6311
  // options.
6312
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6313
      processed_init->protocol.value(),
6314
      url_pattern_helpers::canonicalize_protocol,
6315
      url_pattern_compile_component_options::DEFAULT);
6316
  if (!protocol_component) {
6317
    ada_log("url_pattern_component::compile failed for protocol ",
6318
            processed_init->protocol.value());
6319
    return tl::unexpected(protocol_component.error());
6320
  }
6321
  url_pattern_.protocol_component = std::move(*protocol_component);
6322
6323
  // Set urlPattern's username component to the result of compiling a component
6324
  // given processedInit["username"], canonicalize a username, and default
6325
  // options.
6326
  auto username_component = url_pattern_component<regex_provider>::compile(
6327
      processed_init->username.value(),
6328
      url_pattern_helpers::canonicalize_username,
6329
      url_pattern_compile_component_options::DEFAULT);
6330
  if (!username_component) {
6331
    ada_log("url_pattern_component::compile failed for username ",
6332
            processed_init->username.value());
6333
    return tl::unexpected(username_component.error());
6334
  }
6335
  url_pattern_.username_component = std::move(*username_component);
6336
6337
  // Set urlPattern's password component to the result of compiling a component
6338
  // given processedInit["password"], canonicalize a password, and default
6339
  // options.
6340
  auto password_component = url_pattern_component<regex_provider>::compile(
6341
      processed_init->password.value(),
6342
      url_pattern_helpers::canonicalize_password,
6343
      url_pattern_compile_component_options::DEFAULT);
6344
  if (!password_component) {
6345
    ada_log("url_pattern_component::compile failed for password ",
6346
            processed_init->password.value());
6347
    return tl::unexpected(password_component.error());
6348
  }
6349
  url_pattern_.password_component = std::move(*password_component);
6350
6351
  // TODO: Optimization opportunity. The following if statement can be
6352
  // simplified.
6353
  // If the result running hostname pattern is an IPv6 address given
6354
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6355
  // to the result of compiling a component given processedInit["hostname"],
6356
  // canonicalize an IPv6 hostname, and hostname options.
6357
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6358
    ada_log("processed_init->hostname is ipv6 address");
6359
    // then set urlPattern's hostname component to the result of compiling a
6360
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6361
    // and hostname options.
6362
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6363
        processed_init->hostname.value(),
6364
        url_pattern_helpers::canonicalize_ipv6_hostname,
6365
        url_pattern_compile_component_options::DEFAULT);
6366
    if (!hostname_component) {
6367
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6368
              processed_init->hostname.value());
6369
      return tl::unexpected(hostname_component.error());
6370
    }
6371
    url_pattern_.hostname_component = std::move(*hostname_component);
6372
  } else {
6373
    // Otherwise, set urlPattern's hostname component to the result of compiling
6374
    // a component given processedInit["hostname"], canonicalize a hostname, and
6375
    // hostname options.
6376
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6377
        processed_init->hostname.value(),
6378
        url_pattern_helpers::canonicalize_hostname,
6379
        url_pattern_compile_component_options::HOSTNAME);
6380
    if (!hostname_component) {
6381
      ada_log("url_pattern_component::compile failed for hostname ",
6382
              processed_init->hostname.value());
6383
      return tl::unexpected(hostname_component.error());
6384
    }
6385
    url_pattern_.hostname_component = std::move(*hostname_component);
6386
  }
6387
6388
  // Set urlPattern's port component to the result of compiling a component
6389
  // given processedInit["port"], canonicalize a port, and default options.
6390
  auto port_component = url_pattern_component<regex_provider>::compile(
6391
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6392
      url_pattern_compile_component_options::DEFAULT);
6393
  if (!port_component) {
6394
    ada_log("url_pattern_component::compile failed for port ",
6395
            processed_init->port.value());
6396
    return tl::unexpected(port_component.error());
6397
  }
6398
  url_pattern_.port_component = std::move(*port_component);
6399
6400
  // Let compileOptions be a copy of the default options with the ignore case
6401
  // property set to options["ignoreCase"].
6402
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6403
  if (options) {
6404
    compile_options.ignore_case = options->ignore_case;
6405
  }
6406
6407
  // TODO: Optimization opportunity: Simplify this if statement.
6408
  // If the result of running protocol component matches a special scheme given
6409
  // urlPattern's protocol component is true, then:
6410
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6411
          regex_provider>(url_pattern_.protocol_component)) {
6412
    // Let pathCompileOptions be copy of the pathname options with the ignore
6413
    // case property set to options["ignoreCase"].
6414
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6415
    if (options) {
6416
      path_compile_options.ignore_case = options->ignore_case;
6417
    }
6418
6419
    // Set urlPattern's pathname component to the result of compiling a
6420
    // component given processedInit["pathname"], canonicalize a pathname, and
6421
    // pathCompileOptions.
6422
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6423
        processed_init->pathname.value(),
6424
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6425
    if (!pathname_component) {
6426
      ada_log("url_pattern_component::compile failed for pathname ",
6427
              processed_init->pathname.value());
6428
      return tl::unexpected(pathname_component.error());
6429
    }
6430
    url_pattern_.pathname_component = std::move(*pathname_component);
6431
  } else {
6432
    // Otherwise set urlPattern's pathname component to the result of compiling
6433
    // a component given processedInit["pathname"], canonicalize an opaque
6434
    // pathname, and compileOptions.
6435
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6436
        processed_init->pathname.value(),
6437
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6438
    if (!pathname_component) {
6439
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6440
              processed_init->pathname.value());
6441
      return tl::unexpected(pathname_component.error());
6442
    }
6443
    url_pattern_.pathname_component = std::move(*pathname_component);
6444
  }
6445
6446
  // Set urlPattern's search component to the result of compiling a component
6447
  // given processedInit["search"], canonicalize a search, and compileOptions.
6448
  auto search_component = url_pattern_component<regex_provider>::compile(
6449
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6450
      compile_options);
6451
  if (!search_component) {
6452
    ada_log("url_pattern_component::compile failed for search ",
6453
            processed_init->search.value());
6454
    return tl::unexpected(search_component.error());
6455
  }
6456
  url_pattern_.search_component = std::move(*search_component);
6457
6458
  // Set urlPattern's hash component to the result of compiling a component
6459
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6460
  auto hash_component = url_pattern_component<regex_provider>::compile(
6461
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6462
      compile_options);
6463
  if (!hash_component) {
6464
    ada_log("url_pattern_component::compile failed for hash ",
6465
            processed_init->hash.value());
6466
    return tl::unexpected(hash_component.error());
6467
  }
6468
  url_pattern_.hash_component = std::move(*hash_component);
6469
6470
  // Return urlPattern.
6471
  return url_pattern_;
6472
}
6473
#endif  // ADA_INCLUDE_URL_PATTERN
6474
6475
}  // namespace ada::parser
6476
6477
#endif  // ADA_PARSER_INL_H
6478
/* end file include/ada/parser-inl.h */
6479
/* begin file include/ada/scheme-inl.h */
6480
/**
6481
 * @file scheme-inl.h
6482
 * @brief Definitions for the URL scheme.
6483
 */
6484
#ifndef ADA_SCHEME_INL_H
6485
#define ADA_SCHEME_INL_H
6486
6487
6488
namespace ada::scheme {
6489
6490
/**
6491
 * @namespace ada::scheme::details
6492
 * @brief Includes the definitions for scheme specific entities
6493
 */
6494
namespace details {
6495
// for use with is_special and get_special_port
6496
// Spaces, if present, are removed from URL.
6497
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6498
                                                "ftp",  "wss", "file",  " "};
6499
// for use with get_special_port
6500
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6501
6502
// @private
6503
// convert a string_view to a 64-bit integer key for fast comparison
6504
0
constexpr uint64_t make_key(std::string_view sv) {
6505
0
  uint64_t val = 0;
6506
0
  for (size_t i = 0; i < sv.size(); i++)
6507
0
    val |= (uint64_t)(uint8_t)sv[i] << (i * 8);
6508
0
  return val;
6509
0
}
6510
// precomputed keys for the special schemes, indexed by a hash of the input
6511
// string
6512
constexpr uint64_t scheme_keys[] = {
6513
    make_key("http"),   // 0: HTTP
6514
    0,                  // 1: sentinel
6515
    make_key("https"),  // 2: HTTPS
6516
    make_key("ws"),     // 3: WS
6517
    make_key("ftp"),    // 4: FTP
6518
    make_key("wss"),    // 5: WSS
6519
    make_key("file"),   // 6: FILE
6520
    0,                  // 7: sentinel
6521
};
6522
6523
// @private
6524
// branchless load of up to 5 characters into a uint64_t, padding with zeros if
6525
// n < 5
6526
4.14k
inline uint64_t branchless_load5(const char* p, size_t n) {
6527
4.14k
  uint64_t input = (uint8_t)p[0];
6528
4.14k
  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
6529
4.14k
  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
6530
4.14k
  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
6531
4.14k
  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
6532
4.14k
  return input;
6533
4.14k
}
6534
}  // namespace details
6535
6536
/****
6537
 * @private
6538
 * In is_special, get_scheme_type, and get_special_port, we
6539
 * use a standard hashing technique to find the index of the scheme in
6540
 * the is_special_list. The hashing technique is based on the size of
6541
 * the scheme and the first character of the scheme. It ensures that we
6542
 * do at most one string comparison per call. If the protocol is
6543
 * predictible (e.g., it is always "http"), we can get a better average
6544
 * performance by using a simpler approach where we loop and compare
6545
 * scheme with all possible protocols starting with the most likely
6546
 * protocol. Doing multiple comparisons may have a poor worst case
6547
 * performance, however. In this instance, we choose a potentially
6548
 * slightly lower best-case performance for a better worst-case
6549
 * performance. We can revisit this choice at any time.
6550
 *
6551
 * Reference:
6552
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6553
 * More C++ gems 17 (2000).
6554
 *
6555
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6556
 *
6557
 * Reference: https://github.com/ada-url/ada/issues/617
6558
 ****/
6559
6560
0
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6561
0
  if (scheme.empty()) {
6562
0
    return false;
6563
0
  }
6564
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6565
0
  const std::string_view target = details::is_special_list[hash_value];
6566
0
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6567
0
}
6568
0
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6569
0
  if (scheme.empty()) {
6570
0
    return 0;
6571
0
  }
6572
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6573
0
  const std::string_view target = details::is_special_list[hash_value];
6574
0
  if (scheme.size() == target.size() &&
6575
0
      details::branchless_load5(scheme.data(), scheme.size()) ==
6576
0
          details::scheme_keys[hash_value]) {
6577
0
    return details::special_ports[hash_value];
6578
0
  } else {
6579
0
    return 0;
6580
0
  }
6581
0
}
6582
58
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6583
58
  return details::special_ports[int(type)];
6584
58
}
6585
4.14k
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6586
4.14k
  if (scheme.empty()) {
6587
0
    return ada::scheme::NOT_SPECIAL;
6588
0
  }
6589
4.14k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6590
4.14k
  const std::string_view target = details::is_special_list[hash_value];
6591
4.14k
  if (scheme.size() == target.size() &&
6592
4.14k
      details::branchless_load5(scheme.data(), scheme.size()) ==
6593
4.14k
          details::scheme_keys[hash_value]) {
6594
4.14k
    return ada::scheme::type(hash_value);
6595
4.14k
  } else {
6596
0
    return ada::scheme::NOT_SPECIAL;
6597
0
  }
6598
4.14k
}
6599
6600
}  // namespace ada::scheme
6601
6602
#endif  // ADA_SCHEME_INL_H
6603
/* end file include/ada/scheme-inl.h */
6604
/* begin file include/ada/serializers.h */
6605
/**
6606
 * @file serializers.h
6607
 * @brief IP address serialization utilities.
6608
 *
6609
 * This header provides functions for converting IP addresses to their
6610
 * string representations according to the WHATWG URL Standard.
6611
 */
6612
#ifndef ADA_SERIALIZERS_H
6613
#define ADA_SERIALIZERS_H
6614
6615
6616
#include <array>
6617
#include <string>
6618
6619
/**
6620
 * @namespace ada::serializers
6621
 * @brief IP address serialization functions.
6622
 *
6623
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6624
 */
6625
namespace ada::serializers {
6626
6627
/**
6628
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6629
 * Used for :: compression in IPv6 serialization.
6630
 *
6631
 * @param address The 8 16-bit pieces of the IPv6 address.
6632
 * @param[out] compress Index of the start of the longest zero sequence.
6633
 * @param[out] compress_length Length of the longest zero sequence.
6634
 */
6635
void find_longest_sequence_of_ipv6_pieces(
6636
    const std::array<uint16_t, 8>& address, size_t& compress,
6637
    size_t& compress_length) noexcept;
6638
6639
/**
6640
 * Serializes an IPv6 address to its string representation.
6641
 *
6642
 * @param address The 8 16-bit pieces of the IPv6 address.
6643
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6644
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6645
 */
6646
std::string ipv6(const std::array<uint16_t, 8>& address);
6647
6648
/**
6649
 * Serializes an IPv4 address to its dotted-decimal string representation.
6650
 *
6651
 * @param address The 32-bit IPv4 address as an integer.
6652
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6653
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6654
 */
6655
std::string ipv4(uint64_t address);
6656
6657
}  // namespace ada::serializers
6658
6659
#endif  // ADA_SERIALIZERS_H
6660
/* end file include/ada/serializers.h */
6661
/* begin file include/ada/state.h */
6662
/**
6663
 * @file state.h
6664
 * @brief URL parser state machine states.
6665
 *
6666
 * Defines the states used by the URL parsing state machine as specified
6667
 * in the WHATWG URL Standard.
6668
 *
6669
 * @see https://url.spec.whatwg.org/#url-parsing
6670
 */
6671
#ifndef ADA_STATE_H
6672
#define ADA_STATE_H
6673
6674
6675
#include <string>
6676
6677
namespace ada {
6678
6679
/**
6680
 * @brief States in the URL parsing state machine.
6681
 *
6682
 * The URL parser processes input through a sequence of states, each handling
6683
 * a specific part of the URL syntax.
6684
 *
6685
 * @see https://url.spec.whatwg.org/#url-parsing
6686
 */
6687
enum class state {
6688
  /**
6689
   * @see https://url.spec.whatwg.org/#authority-state
6690
   */
6691
  AUTHORITY,
6692
6693
  /**
6694
   * @see https://url.spec.whatwg.org/#scheme-start-state
6695
   */
6696
  SCHEME_START,
6697
6698
  /**
6699
   * @see https://url.spec.whatwg.org/#scheme-state
6700
   */
6701
  SCHEME,
6702
6703
  /**
6704
   * @see https://url.spec.whatwg.org/#host-state
6705
   */
6706
  HOST,
6707
6708
  /**
6709
   * @see https://url.spec.whatwg.org/#no-scheme-state
6710
   */
6711
  NO_SCHEME,
6712
6713
  /**
6714
   * @see https://url.spec.whatwg.org/#fragment-state
6715
   */
6716
  FRAGMENT,
6717
6718
  /**
6719
   * @see https://url.spec.whatwg.org/#relative-state
6720
   */
6721
  RELATIVE_SCHEME,
6722
6723
  /**
6724
   * @see https://url.spec.whatwg.org/#relative-slash-state
6725
   */
6726
  RELATIVE_SLASH,
6727
6728
  /**
6729
   * @see https://url.spec.whatwg.org/#file-state
6730
   */
6731
  FILE,
6732
6733
  /**
6734
   * @see https://url.spec.whatwg.org/#file-host-state
6735
   */
6736
  FILE_HOST,
6737
6738
  /**
6739
   * @see https://url.spec.whatwg.org/#file-slash-state
6740
   */
6741
  FILE_SLASH,
6742
6743
  /**
6744
   * @see https://url.spec.whatwg.org/#path-or-authority-state
6745
   */
6746
  PATH_OR_AUTHORITY,
6747
6748
  /**
6749
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
6750
   */
6751
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
6752
6753
  /**
6754
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
6755
   */
6756
  SPECIAL_AUTHORITY_SLASHES,
6757
6758
  /**
6759
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
6760
   */
6761
  SPECIAL_RELATIVE_OR_AUTHORITY,
6762
6763
  /**
6764
   * @see https://url.spec.whatwg.org/#query-state
6765
   */
6766
  QUERY,
6767
6768
  /**
6769
   * @see https://url.spec.whatwg.org/#path-state
6770
   */
6771
  PATH,
6772
6773
  /**
6774
   * @see https://url.spec.whatwg.org/#path-start-state
6775
   */
6776
  PATH_START,
6777
6778
  /**
6779
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
6780
   */
6781
  OPAQUE_PATH,
6782
6783
  /**
6784
   * @see https://url.spec.whatwg.org/#port-state
6785
   */
6786
  PORT,
6787
};
6788
6789
/**
6790
 * Converts a parser state to its string name for debugging.
6791
 * @param s The state to convert.
6792
 * @return A string representation of the state.
6793
 */
6794
ada_warn_unused std::string to_string(ada::state s);
6795
6796
}  // namespace ada
6797
6798
#endif  // ADA_STATE_H
6799
/* end file include/ada/state.h */
6800
/* begin file include/ada/unicode.h */
6801
/**
6802
 * @file unicode.h
6803
 * @brief Definitions for all unicode specific functions.
6804
 */
6805
#ifndef ADA_UNICODE_H
6806
#define ADA_UNICODE_H
6807
6808
6809
#include <string>
6810
#include <string_view>
6811
#include <optional>
6812
6813
/**
6814
 * Unicode operations. These functions are not part of our public API and may
6815
 * change at any time.
6816
 *
6817
 * @private
6818
 * @namespace ada::unicode
6819
 * @brief Includes the definitions for unicode operations
6820
 */
6821
namespace ada::unicode {
6822
6823
/**
6824
 * @private
6825
 * We receive a UTF-8 string representing a domain name.
6826
 * If the string is percent encoded, we apply percent decoding.
6827
 *
6828
 * Given a domain, we need to identify its labels.
6829
 * They are separated by label-separators:
6830
 *
6831
 * U+002E (.) FULL STOP
6832
 * U+FF0E FULLWIDTH FULL STOP
6833
 * U+3002 IDEOGRAPHIC FULL STOP
6834
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
6835
 *
6836
 * They are all mapped to U+002E.
6837
 *
6838
 * We process each label into a string that should not exceed 63 octets.
6839
 * If the string is already punycode (starts with "xn--"), then we must
6840
 * scan it to look for unallowed code points.
6841
 * Otherwise, if the string is not pure ASCII, we need to transcode it
6842
 * to punycode by following RFC 3454 which requires us to
6843
 * - Map characters  (see section 3),
6844
 * - Normalize (see section 4),
6845
 * - Reject forbidden characters,
6846
 * - Check for right-to-left characters and if so, check all requirements (see
6847
 * section 6),
6848
 * - Optionally reject based on unassigned code points (section 7).
6849
 *
6850
 * The Unicode standard provides a table of code points with a mapping, a list
6851
 * of forbidden code points and so forth. This table is subject to change and
6852
 * will vary based on the implementation. For Unicode 15, the table is at
6853
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
6854
 * If you use ICU, they parse this table and map it to code using a Python
6855
 * script.
6856
 *
6857
 * The resulting strings should not exceed 255 octets according to RFC 1035
6858
 * section 2.3.4. ICU checks for label size and domain size, but these errors
6859
 * are ignored.
6860
 *
6861
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
6862
 *
6863
 */
6864
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
6865
              size_t first_percent);
6866
6867
/**
6868
 * @private
6869
 * Checks if the input has tab or newline characters.
6870
 *
6871
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
6872
 * enough that compilers like GCC can 'autovectorize it'.
6873
 */
6874
ada_really_inline bool has_tabs_or_newline(
6875
    std::string_view user_input) noexcept;
6876
6877
/**
6878
 * @private
6879
 * Checks if the input is a forbidden host code point.
6880
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
6881
 */
6882
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
6883
6884
/**
6885
 * @private
6886
 * Checks if the input contains a forbidden domain code point.
6887
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6888
 */
6889
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
6890
    const char* input, size_t length) noexcept;
6891
6892
/**
6893
 * @private
6894
 * Checks if the input contains a forbidden domain code point in which case
6895
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
6896
 * then the second bit is set to 1.
6897
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6898
 */
6899
ada_really_inline constexpr uint8_t
6900
contains_forbidden_domain_code_point_or_upper(const char* input,
6901
                                              size_t length) noexcept;
6902
6903
/**
6904
 * @private
6905
 * Checks if the input is a forbidden domain code point.
6906
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6907
 */
6908
ada_really_inline constexpr bool is_forbidden_domain_code_point(
6909
    char c) noexcept;
6910
6911
/**
6912
 * @private
6913
 * Checks if the input is alphanumeric, '+', '-' or '.'
6914
 */
6915
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
6916
6917
/**
6918
 * @private
6919
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
6920
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
6921
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
6922
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
6923
 */
6924
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
6925
6926
/**
6927
 * @private
6928
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
6929
 * inclusive.
6930
 */
6931
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
6932
6933
/**
6934
 * @private
6935
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
6936
 * character.
6937
 */
6938
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
6939
6940
/**
6941
 * @private
6942
 * Checks if the input is a C0 control or space character.
6943
 *
6944
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
6945
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
6946
 * SEPARATOR ONE, inclusive.
6947
 */
6948
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
6949
6950
/**
6951
 * @private
6952
 * Checks if the input is a ASCII tab or newline character.
6953
 *
6954
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
6955
 */
6956
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
6957
6958
/**
6959
 * @private
6960
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
6961
 * match for ".%2e", "%2e.", or "%2e%2e".
6962
 */
6963
ada_really_inline constexpr bool is_double_dot_path_segment(
6964
    std::string_view input) noexcept;
6965
6966
/**
6967
 * @private
6968
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
6969
 * match for "%2e".
6970
 */
6971
ada_really_inline constexpr bool is_single_dot_path_segment(
6972
    std::string_view input) noexcept;
6973
6974
/**
6975
 * @private
6976
 * @details ipv4 character might contain 0-9 or a-f character ranges.
6977
 */
6978
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
6979
6980
/**
6981
 * @private
6982
 * @details Convert hex to binary. Caller is responsible to ensure that
6983
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
6984
 */
6985
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
6986
6987
/**
6988
 * @private
6989
 * first_percent should be  = input.find('%')
6990
 *
6991
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
6992
 * @author Node.js
6993
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
6994
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
6995
 */
6996
std::string percent_decode(std::string_view input, size_t first_percent);
6997
6998
/**
6999
 * @private
7000
 * Returns a percent-encoding string whether percent encoding was needed or not.
7001
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7002
 */
7003
std::string percent_encode(std::string_view input,
7004
                           const uint8_t character_set[]);
7005
/**
7006
 * @private
7007
 * Returns a percent-encoded string version of input, while starting the percent
7008
 * encoding at the provided index.
7009
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7010
 */
7011
std::string percent_encode(std::string_view input,
7012
                           const uint8_t character_set[], size_t index);
7013
/**
7014
 * @private
7015
 * Returns true if percent encoding was needed, in which case, we store
7016
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
7017
 * true, the content is appended to 'out'.
7018
 * If percent encoding is not needed, out is left unchanged.
7019
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7020
 */
7021
template <bool append>
7022
bool percent_encode(std::string_view input, const uint8_t character_set[],
7023
                    std::string& out);
7024
/**
7025
 * @private
7026
 * Returns the index at which percent encoding should start, or (equivalently),
7027
 * the length of the prefix that does not require percent encoding.
7028
 */
7029
ada_really_inline size_t percent_encode_index(std::string_view input,
7030
                                              const uint8_t character_set[]);
7031
/**
7032
 * @private
7033
 * Lowers the string in-place, assuming that the content is ASCII.
7034
 * Return true if the content was ASCII.
7035
 */
7036
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
7037
}  // namespace ada::unicode
7038
7039
#endif  // ADA_UNICODE_H
7040
/* end file include/ada/unicode.h */
7041
/* begin file include/ada/url_base-inl.h */
7042
/**
7043
 * @file url_base-inl.h
7044
 * @brief Inline functions for url base
7045
 */
7046
#ifndef ADA_URL_BASE_INL_H
7047
#define ADA_URL_BASE_INL_H
7048
7049
7050
#include <string>
7051
#if ADA_REGULAR_VISUAL_STUDIO
7052
#include <intrin.h>
7053
#endif  // ADA_REGULAR_VISUAL_STUDIO
7054
7055
namespace ada {
7056
7057
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
7058
15.6k
    const noexcept {
7059
15.6k
  return type != ada::scheme::NOT_SPECIAL;
7060
15.6k
}
7061
7062
0
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
7063
0
  return ada::scheme::get_special_port(type);
7064
0
}
7065
7066
[[nodiscard]] ada_really_inline uint16_t
7067
58
url_base::scheme_default_port() const noexcept {
7068
58
  return scheme::get_special_port(type);
7069
58
}
7070
7071
}  // namespace ada
7072
7073
#endif  // ADA_URL_BASE_INL_H
7074
/* end file include/ada/url_base-inl.h */
7075
/* begin file include/ada/url-inl.h */
7076
/**
7077
 * @file url-inl.h
7078
 * @brief Definitions for the URL
7079
 */
7080
#ifndef ADA_URL_INL_H
7081
#define ADA_URL_INL_H
7082
7083
7084
#include <charconv>
7085
#include <optional>
7086
#include <string>
7087
#if ADA_REGULAR_VISUAL_STUDIO
7088
#include <intrin.h>
7089
#endif  // ADA_REGULAR_VISUAL_STUDIO
7090
7091
namespace ada {
7092
0
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7093
0
  return !username.empty() || !password.empty();
7094
0
}
7095
0
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7096
0
  return port.has_value();
7097
0
}
7098
0
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7099
0
  return !host.has_value() || host->empty() || type == ada::scheme::type::FILE;
7100
0
}
7101
0
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7102
0
  if (!host.has_value()) {
7103
0
    return false;
7104
0
  }
7105
0
  return host->empty();
7106
0
}
7107
0
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7108
0
  return host.has_value();
7109
0
}
7110
0
inline std::ostream& operator<<(std::ostream& out, const ada::url& u) {
7111
0
  return out << u.to_string();
7112
0
}
7113
7114
0
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7115
0
  return path.size();
7116
0
}
7117
7118
0
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7119
0
  return path;
7120
0
}
7121
7122
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7123
0
    const {
7124
0
  url_components out{};
7125
0
7126
0
  // protocol ends with ':'. for example: "https:"
7127
0
  out.protocol_end = uint32_t(get_protocol().size());
7128
0
7129
0
  // Trailing index is always the next character of the current one.
7130
0
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7131
0
  size_t running_index = out.protocol_end;
7132
0
7133
0
  if (host.has_value()) {
7134
0
    // 2 characters for "//" and 1 character for starting index
7135
0
    out.host_start = out.protocol_end + 2;
7136
0
7137
0
    if (has_credentials()) {
7138
0
      out.username_end = uint32_t(out.host_start + username.size());
7139
0
7140
0
      out.host_start += uint32_t(username.size());
7141
0
7142
0
      if (!password.empty()) {
7143
0
        out.host_start += uint32_t(password.size() + 1);
7144
0
      }
7145
0
7146
0
      out.host_end = uint32_t(out.host_start + host->size());
7147
0
    } else {
7148
0
      out.username_end = out.host_start;
7149
0
7150
0
      // Host does not start with "@" if it does not include credentials.
7151
0
      out.host_end = uint32_t(out.host_start + host->size()) - 1;
7152
0
    }
7153
0
7154
0
    running_index = out.host_end + 1;
7155
0
  } else {
7156
0
    // Update host start and end date to the same index, since it does not
7157
0
    // exist.
7158
0
    out.host_start = out.protocol_end;
7159
0
    out.host_end = out.host_start;
7160
0
7161
0
    if (!has_opaque_path && path.starts_with("//")) {
7162
0
      // If url's host is null, url does not have an opaque path, url's path's
7163
0
      // size is greater than 1, and url's path[0] is the empty string, then
7164
0
      // append U+002F (/) followed by U+002E (.) to output.
7165
0
      running_index = out.protocol_end + 2;
7166
0
    } else {
7167
0
      running_index = out.protocol_end;
7168
0
    }
7169
0
  }
7170
0
7171
0
  if (port.has_value()) {
7172
0
    out.port = *port;
7173
0
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7174
0
  }
7175
0
7176
0
  out.pathname_start = uint32_t(running_index);
7177
0
7178
0
  running_index += path.size();
7179
0
7180
0
  if (query.has_value()) {
7181
0
    out.search_start = uint32_t(running_index);
7182
0
    running_index += get_search().size();
7183
0
    if (get_search().empty()) {
7184
0
      running_index++;
7185
0
    }
7186
0
  }
7187
0
7188
0
  if (hash.has_value()) {
7189
0
    out.hash_start = uint32_t(running_index);
7190
0
  }
7191
0
7192
0
  return out;
7193
0
}
7194
7195
0
inline void url::update_base_hostname(std::string_view input) { host = input; }
7196
7197
0
inline void url::update_unencoded_base_hash(std::string_view input) {
7198
  // We do the percent encoding
7199
0
  hash = unicode::percent_encode(input,
7200
0
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7201
0
}
7202
7203
inline void url::update_base_search(std::string_view input,
7204
0
                                    const uint8_t query_percent_encode_set[]) {
7205
0
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7206
0
}
7207
7208
0
inline void url::update_base_search(std::optional<std::string>&& input) {
7209
0
  query = std::move(input);
7210
0
}
7211
7212
0
inline void url::update_base_pathname(const std::string_view input) {
7213
0
  path = input;
7214
0
}
7215
7216
0
inline void url::update_base_username(const std::string_view input) {
7217
0
  username = input;
7218
0
}
7219
7220
0
inline void url::update_base_password(const std::string_view input) {
7221
0
  password = input;
7222
0
}
7223
7224
0
inline void url::update_base_port(std::optional<uint16_t> input) {
7225
0
  port = input;
7226
0
}
7227
7228
0
constexpr void url::clear_pathname() { path.clear(); }
7229
7230
0
constexpr void url::clear_search() { query = std::nullopt; }
7231
7232
0
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7233
0
  return hash.has_value();
7234
0
}
7235
7236
0
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7237
0
  return query.has_value();
7238
0
}
7239
7240
0
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7241
7242
0
inline void url::set_scheme(std::string&& new_scheme) noexcept {
7243
0
  type = ada::scheme::get_scheme_type(new_scheme);
7244
  // We only move the 'scheme' if it is non-special.
7245
0
  if (!is_special()) {
7246
0
    non_special_scheme = std::move(new_scheme);
7247
0
  }
7248
0
}
7249
7250
0
constexpr void url::copy_scheme(ada::url&& u) {
7251
0
  non_special_scheme = u.non_special_scheme;
7252
0
  type = u.type;
7253
0
}
7254
7255
0
constexpr void url::copy_scheme(const ada::url& u) {
7256
0
  non_special_scheme = u.non_special_scheme;
7257
0
  type = u.type;
7258
0
}
7259
7260
0
[[nodiscard]] ada_really_inline std::string url::get_href() const {
7261
0
  std::string output = get_protocol();
7262
0
7263
0
  if (host.has_value()) {
7264
0
    output += "//";
7265
0
    if (has_credentials()) {
7266
0
      output += username;
7267
0
      if (!password.empty()) {
7268
0
        output += ":" + get_password();
7269
0
      }
7270
0
      output += "@";
7271
0
    }
7272
0
    output += host.value();
7273
0
    if (port.has_value()) {
7274
0
      output += ":" + get_port();
7275
0
    }
7276
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7277
0
    // If url's host is null, url does not have an opaque path, url's path's
7278
0
    // size is greater than 1, and url's path[0] is the empty string, then
7279
0
    // append U+002F (/) followed by U+002E (.) to output.
7280
0
    output += "/.";
7281
0
  }
7282
0
  output += path;
7283
0
  if (query.has_value()) {
7284
0
    output += "?" + query.value();
7285
0
  }
7286
0
  if (hash.has_value()) {
7287
0
    output += "#" + hash.value();
7288
0
  }
7289
0
  return output;
7290
0
}
7291
7292
0
[[nodiscard]] inline size_t url::get_href_size() const noexcept {
7293
  // Mirrors the logic of get_href() but only computes the total size.
7294
0
  size_t size = 0;
7295
  // Protocol: scheme + ":"
7296
0
  if (is_special()) {
7297
0
    size += ada::scheme::details::is_special_list[type].size() + 1;
7298
0
  } else {
7299
0
    size += non_special_scheme.size() + 1;
7300
0
  }
7301
0
  if (host.has_value()) {
7302
0
    size += host->size();
7303
0
    size += 2;  // "//"
7304
0
    if (has_credentials()) {
7305
0
      size += username.size();
7306
0
      if (!password.empty()) {
7307
0
        size += 1 + password.size();  // ":" + password
7308
0
      }
7309
0
      size += 1;  // "@"
7310
0
    }
7311
0
    if (port.has_value()) {
7312
0
      size += 1;  // ":"
7313
      // Count digits of port value without calling std::to_string.
7314
0
      uint16_t p = port.value();
7315
0
      size += (p >= 10000)  ? 5
7316
0
              : (p >= 1000) ? 4
7317
0
              : (p >= 100)  ? 3
7318
0
              : (p >= 10)   ? 2
7319
0
                            : 1;
7320
0
    }
7321
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7322
0
    size += 2;  // "/."
7323
0
  }
7324
0
  size += path.size();
7325
0
  if (query.has_value()) {
7326
0
    size += 1 + query->size();  // "?" + query
7327
0
  }
7328
0
  if (hash.has_value()) {
7329
0
    size += 1 + hash->size();  // "#" + hash
7330
0
  }
7331
0
  return size;
7332
0
}
7333
7334
ada_really_inline size_t url::parse_port(std::string_view view,
7335
0
                                         bool check_trailing_content) noexcept {
7336
0
  ada_log("parse_port('", view, "') ", view.size());
7337
0
  if (!view.empty() && view[0] == '-') {
7338
0
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7339
0
    is_valid = false;
7340
0
    return 0;
7341
0
  }
7342
0
  uint16_t parsed_port{};
7343
0
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7344
0
  if (r.ec == std::errc::result_out_of_range) {
7345
0
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7346
0
    is_valid = false;
7347
0
    return 0;
7348
0
  }
7349
0
  ada_log("parse_port: ", parsed_port);
7350
0
  const auto consumed = size_t(r.ptr - view.data());
7351
0
  ada_log("parse_port: consumed ", consumed);
7352
0
  if (check_trailing_content) {
7353
0
    is_valid &=
7354
0
        (consumed == view.size() || view[consumed] == '/' ||
7355
0
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7356
0
  }
7357
0
  ada_log("parse_port: is_valid = ", is_valid);
7358
0
  if (is_valid) {
7359
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7360
0
    auto default_port = scheme_default_port();
7361
0
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7362
0
                         (default_port != parsed_port);
7363
0
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7364
0
                                                  : std::nullopt;
7365
0
  }
7366
0
  return consumed;
7367
0
}
7368
7369
}  // namespace ada
7370
7371
#endif  // ADA_URL_H
7372
/* end file include/ada/url-inl.h */
7373
/* begin file include/ada/url_components-inl.h */
7374
/**
7375
 * @file url_components.h
7376
 * @brief Declaration for the URL Components
7377
 */
7378
#ifndef ADA_URL_COMPONENTS_INL_H
7379
#define ADA_URL_COMPONENTS_INL_H
7380
7381
7382
namespace ada {
7383
7384
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7385
576
    const noexcept {
7386
  /**
7387
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7388
   *       |     |    |          | ^^^^|       |   |
7389
   *       |     |    |          | |   |       |   `----- hash_start
7390
   *       |     |    |          | |   |       `--------- search_start
7391
   *       |     |    |          | |   `----------------- pathname_start
7392
   *       |     |    |          | `--------------------- port
7393
   *       |     |    |          `----------------------- host_end
7394
   *       |     |    `---------------------------------- host_start
7395
   *       |     `--------------------------------------- username_end
7396
   *       `--------------------------------------------- protocol_end
7397
   */
7398
  // These conditions can be made more strict.
7399
576
  if (protocol_end == url_components::omitted) {
7400
0
    return false;
7401
0
  }
7402
576
  uint32_t index = protocol_end;
7403
7404
576
  if (username_end == url_components::omitted) {
7405
0
    return false;
7406
0
  }
7407
576
  if (username_end < index) {
7408
0
    return false;
7409
0
  }
7410
576
  index = username_end;
7411
7412
576
  if (host_start == url_components::omitted) {
7413
0
    return false;
7414
0
  }
7415
576
  if (host_start < index) {
7416
0
    return false;
7417
0
  }
7418
576
  index = host_start;
7419
7420
576
  if (port != url_components::omitted) {
7421
24
    if (port > 0xffff) {
7422
0
      return false;
7423
0
    }
7424
24
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7425
24
    if (index + port_length < index) {
7426
0
      return false;
7427
0
    }
7428
24
    index += port_length;
7429
24
  }
7430
7431
576
  if (pathname_start == url_components::omitted) {
7432
0
    return false;
7433
0
  }
7434
576
  if (pathname_start < index) {
7435
0
    return false;
7436
0
  }
7437
576
  index = pathname_start;
7438
7439
576
  if (search_start != url_components::omitted) {
7440
212
    if (search_start < index) {
7441
0
      return false;
7442
0
    }
7443
212
    index = search_start;
7444
212
  }
7445
7446
576
  if (hash_start != url_components::omitted) {
7447
51
    if (hash_start < index) {
7448
0
      return false;
7449
0
    }
7450
51
  }
7451
7452
576
  return true;
7453
576
}
7454
7455
}  // namespace ada
7456
#endif
7457
/* end file include/ada/url_components-inl.h */
7458
/* begin file include/ada/url_aggregator.h */
7459
/**
7460
 * @file url_aggregator.h
7461
 * @brief Declaration for the `ada::url_aggregator` class.
7462
 *
7463
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7464
 * URL using a single buffer with component offsets. This is the default and
7465
 * most memory-efficient URL representation in Ada.
7466
 *
7467
 * @see url.h for an alternative representation using separate strings
7468
 */
7469
#ifndef ADA_URL_AGGREGATOR_H
7470
#define ADA_URL_AGGREGATOR_H
7471
7472
#include <ostream>
7473
#include <string>
7474
#include <string_view>
7475
#include <variant>
7476
7477
7478
namespace ada {
7479
7480
namespace parser {}
7481
7482
/**
7483
 * @brief Memory-efficient URL representation using a single buffer.
7484
 *
7485
 * The `url_aggregator` stores the entire normalized URL in a single string
7486
 * buffer and tracks component boundaries using offsets. This design minimizes
7487
 * memory allocations and is ideal for read-mostly access patterns.
7488
 *
7489
 * Getter methods return `std::string_view` pointing into the internal buffer.
7490
 * These views are lightweight (no allocation) but become invalid if the
7491
 * url_aggregator is modified or destroyed.
7492
 *
7493
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7494
 * when any setter is called. Do not use a getter's result as input to a
7495
 * setter on the same object without copying first.
7496
 *
7497
 * @note This is the default URL type returned by `ada::parse()`.
7498
 *
7499
 * @see url For an alternative using separate std::string instances
7500
 */
7501
struct url_aggregator : url_base {
7502
4.14k
  url_aggregator() = default;
7503
0
  url_aggregator(const url_aggregator& u) = default;
7504
576
  url_aggregator(url_aggregator&& u) noexcept = default;
7505
0
  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
7506
0
  url_aggregator& operator=(const url_aggregator& u) = default;
7507
4.72k
  ~url_aggregator() override = default;
7508
7509
  /**
7510
   * The setter functions follow the steps defined in the URL Standard.
7511
   *
7512
   * The url_aggregator has a single buffer that contains the entire normalized
7513
   * URL. The various components are represented as offsets into that buffer.
7514
   * When you call get_pathname(), for example, you get a std::string_view that
7515
   * points into that buffer. If the url_aggregator is modified, the buffer may
7516
   * be reallocated, and the std::string_view you obtained earlier may become
7517
   * invalid. In particular, this implies that you cannot modify the URL using
7518
   * a setter function with a std::string_view that points into the
7519
   * url_aggregator E.g., the following is incorrect:
7520
   * url->set_hostname(url->get_pathname()).
7521
   * You must first copy the pathname to a separate string.
7522
   * std::string pathname(url->get_pathname());
7523
   * url->set_hostname(pathname);
7524
   *
7525
   * The caller is responsible for ensuring that the url_aggregator is not
7526
   * modified while any std::string_view obtained from it is in use.
7527
   */
7528
  bool set_href(std::string_view input);
7529
  bool set_host(std::string_view input);
7530
  bool set_hostname(std::string_view input);
7531
  bool set_protocol(std::string_view input);
7532
  bool set_username(std::string_view input);
7533
  bool set_password(std::string_view input);
7534
  bool set_port(std::string_view input);
7535
  bool set_pathname(std::string_view input);
7536
  void set_search(std::string_view input);
7537
  void set_hash(std::string_view input);
7538
7539
  /**
7540
   * Validates whether the hostname is a valid domain according to RFC 1034.
7541
   * @return `true` if the domain is valid, `false` otherwise.
7542
   */
7543
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7544
7545
  /**
7546
   * Returns the URL's origin (scheme + host + port for special URLs).
7547
   * @return A newly allocated string containing the serialized origin.
7548
   * @see https://url.spec.whatwg.org/#concept-url-origin
7549
   */
7550
  [[nodiscard]] std::string get_origin() const override;
7551
7552
  /**
7553
   * Returns the full serialized URL (the href) as a string_view.
7554
   * Does not allocate memory. The returned view becomes invalid if this
7555
   * url_aggregator is modified or destroyed.
7556
   * @return A string_view into the internal buffer.
7557
   * @see https://url.spec.whatwg.org/#dom-url-href
7558
   */
7559
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7560
      ada_lifetime_bound;
7561
7562
  /**
7563
   * Returns the byte length of the serialized URL without allocating a string.
7564
   * @return Size of the href in bytes.
7565
   */
7566
  [[nodiscard]] constexpr size_t get_href_size() const noexcept;
7567
7568
  /**
7569
   * Returns the URL's username component.
7570
   * Does not allocate memory. The returned view becomes invalid if this
7571
   * url_aggregator is modified or destroyed.
7572
   * @return A string_view of the username.
7573
   * @see https://url.spec.whatwg.org/#dom-url-username
7574
   */
7575
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
7576
7577
  /**
7578
   * Returns the URL's password component.
7579
   * Does not allocate memory. The returned view becomes invalid if this
7580
   * url_aggregator is modified or destroyed.
7581
   * @return A string_view of the password.
7582
   * @see https://url.spec.whatwg.org/#dom-url-password
7583
   */
7584
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
7585
7586
  /**
7587
   * Returns the URL's port as a string (e.g., "8080").
7588
   * Does not allocate memory. Returns empty view if no port is set.
7589
   * The returned view becomes invalid if this url_aggregator is modified.
7590
   * @return A string_view of the port.
7591
   * @see https://url.spec.whatwg.org/#dom-url-port
7592
   */
7593
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
7594
7595
  /**
7596
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
7597
   * Does not allocate memory. Returns empty view if no fragment is set.
7598
   * The returned view becomes invalid if this url_aggregator is modified.
7599
   * @return A string_view of the hash.
7600
   * @see https://url.spec.whatwg.org/#dom-url-hash
7601
   */
7602
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
7603
7604
  /**
7605
   * Returns the URL's host and port (e.g., "example.com:8080").
7606
   * Does not allocate memory. Returns empty view if no host is set.
7607
   * The returned view becomes invalid if this url_aggregator is modified.
7608
   * @return A string_view of host:port.
7609
   * @see https://url.spec.whatwg.org/#dom-url-host
7610
   */
7611
  [[nodiscard]] std::string_view get_host() const ada_lifetime_bound;
7612
7613
  /**
7614
   * Returns the URL's hostname (without port).
7615
   * Does not allocate memory. Returns empty view if no host is set.
7616
   * The returned view becomes invalid if this url_aggregator is modified.
7617
   * @return A string_view of the hostname.
7618
   * @see https://url.spec.whatwg.org/#dom-url-hostname
7619
   */
7620
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
7621
7622
  /**
7623
   * Returns the URL's path component.
7624
   * Does not allocate memory. The returned view becomes invalid if this
7625
   * url_aggregator is modified or destroyed.
7626
   * @return A string_view of the pathname.
7627
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7628
   */
7629
  [[nodiscard]] constexpr std::string_view get_pathname() const
7630
      ada_lifetime_bound;
7631
7632
  /**
7633
   * Returns the byte length of the pathname without creating a string.
7634
   * @return Size of the pathname in bytes.
7635
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7636
   */
7637
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
7638
7639
  /**
7640
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
7641
   * Does not allocate memory. Returns empty view if no query is set.
7642
   * The returned view becomes invalid if this url_aggregator is modified.
7643
   * @return A string_view of the search/query.
7644
   * @see https://url.spec.whatwg.org/#dom-url-search
7645
   */
7646
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
7647
7648
  /**
7649
   * Returns the URL's scheme followed by a colon (e.g., "https:").
7650
   * Does not allocate memory. The returned view becomes invalid if this
7651
   * url_aggregator is modified or destroyed.
7652
   * @return A string_view of the protocol.
7653
   * @see https://url.spec.whatwg.org/#dom-url-protocol
7654
   */
7655
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
7656
7657
  /**
7658
   * Checks if the URL has credentials (non-empty username or password).
7659
   * @return `true` if username or password is non-empty, `false` otherwise.
7660
   */
7661
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
7662
      const noexcept;
7663
7664
  /**
7665
   * Returns the URL component offsets for efficient serialization.
7666
   *
7667
   * The components represent byte offsets into the serialized URL:
7668
   * ```
7669
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7670
   *       |     |    |          | ^^^^|       |   |
7671
   *       |     |    |          | |   |       |   `----- hash_start
7672
   *       |     |    |          | |   |       `--------- search_start
7673
   *       |     |    |          | |   `----------------- pathname_start
7674
   *       |     |    |          | `--------------------- port
7675
   *       |     |    |          `----------------------- host_end
7676
   *       |     |    `---------------------------------- host_start
7677
   *       |     `--------------------------------------- username_end
7678
   *       `--------------------------------------------- protocol_end
7679
   * ```
7680
   * @return A constant reference to the url_components struct.
7681
   * @see https://github.com/servo/rust-url
7682
   */
7683
  [[nodiscard]] ada_really_inline const url_components& get_components()
7684
      const noexcept;
7685
7686
  /**
7687
   * Returns a JSON string representation of this URL for debugging.
7688
   * @return A JSON-formatted string with all URL components.
7689
   */
7690
  [[nodiscard]] std::string to_string() const override;
7691
7692
  /**
7693
   * Returns a visual diagram showing component boundaries in the URL.
7694
   * Useful for debugging and understanding URL structure.
7695
   * @return A multi-line string diagram.
7696
   */
7697
  [[nodiscard]] std::string to_diagram() const;
7698
7699
  /**
7700
   * Validates internal consistency of component offsets (for debugging).
7701
   * @return `true` if offsets are consistent, `false` if corrupted.
7702
   */
7703
  [[nodiscard]] constexpr bool validate() const noexcept;
7704
7705
  /**
7706
   * Checks if the URL has an empty hostname (host is set but empty string).
7707
   * @return `true` if host exists but is empty, `false` otherwise.
7708
   */
7709
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
7710
7711
  /**
7712
   * Checks if the URL has a hostname (including empty hostnames).
7713
   * @return `true` if host is present, `false` otherwise.
7714
   */
7715
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
7716
7717
  /**
7718
   * Checks if the URL has a non-empty username.
7719
   * @return `true` if username is non-empty, `false` otherwise.
7720
   */
7721
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
7722
7723
  /**
7724
   * Checks if the URL has a non-empty password.
7725
   * @return `true` if password is non-empty, `false` otherwise.
7726
   */
7727
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
7728
7729
  /**
7730
   * Checks if the URL has a non-default port explicitly specified.
7731
   * @return `true` if a port is present, `false` otherwise.
7732
   */
7733
  [[nodiscard]] constexpr bool has_port() const noexcept;
7734
7735
  /**
7736
   * Checks if the URL has a password component (may be empty).
7737
   * @return `true` if password is present, `false` otherwise.
7738
   */
7739
  [[nodiscard]] constexpr bool has_password() const noexcept;
7740
7741
  /**
7742
   * Checks if the URL has a fragment/hash component.
7743
   * @return `true` if hash is present, `false` otherwise.
7744
   */
7745
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
7746
7747
  /**
7748
   * Checks if the URL has a query/search component.
7749
   * @return `true` if query is present, `false` otherwise.
7750
   */
7751
  [[nodiscard]] constexpr bool has_search() const noexcept override;
7752
7753
  /**
7754
   * Removes the port from the URL.
7755
   */
7756
  inline void clear_port();
7757
7758
  /**
7759
   * Removes the hash/fragment from the URL.
7760
   */
7761
  inline void clear_hash();
7762
7763
  /**
7764
   * Removes the query/search string from the URL.
7765
   */
7766
  inline void clear_search() override;
7767
7768
 private:
7769
  // helper methods
7770
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
7771
      url_aggregator& url);
7772
  // parse_url methods
7773
  friend url_aggregator parser::parse_url<url_aggregator>(
7774
      std::string_view, const url_aggregator*);
7775
7776
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
7777
      std::string_view, const url_aggregator*);
7778
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
7779
      std::string_view, const url_aggregator*);
7780
7781
#if ADA_INCLUDE_URL_PATTERN
7782
  // url_pattern methods
7783
  template <url_pattern_regex::regex_concept regex_provider>
7784
  friend tl::expected<url_pattern<regex_provider>, errors>
7785
  parse_url_pattern_impl(
7786
      std::variant<std::string_view, url_pattern_init>&& input,
7787
      const std::string_view* base_url, const url_pattern_options* options);
7788
#endif  // ADA_INCLUDE_URL_PATTERN
7789
7790
  // components is declared before buffer so that the offset fields land
7791
  // close to url_base in memory, improving cache locality for getter calls.
7792
  // Note: exact cache-line placement is implementation- and platform-dependent.
7793
  url_components components{};
7794
  std::string buffer{};
7795
7796
  /**
7797
   * Returns true if neither the search, nor the hash nor the pathname
7798
   * have been set.
7799
   * @return true if the buffer is ready to receive the path.
7800
   */
7801
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
7802
7803
  inline void add_authority_slashes_if_needed();
7804
7805
  /**
7806
   * To optimize performance, you may indicate how much memory to allocate
7807
   * within this instance.
7808
   */
7809
  constexpr void reserve(uint32_t capacity);
7810
7811
  ada_really_inline size_t parse_port(std::string_view view,
7812
                                      bool check_trailing_content) override;
7813
7814
0
  ada_really_inline size_t parse_port(std::string_view view) override {
7815
0
    return this->parse_port(view, false);
7816
0
  }
7817
7818
  /**
7819
   * Return true on success. The 'in_place' parameter indicates whether the
7820
   * the string_view input is pointing in the buffer. When in_place is false,
7821
   * we must nearly always update the buffer.
7822
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
7823
   */
7824
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
7825
7826
  /**
7827
   * Return true on success.
7828
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
7829
   */
7830
  [[nodiscard]] bool parse_ipv6(std::string_view input);
7831
7832
  /**
7833
   * Return true on success.
7834
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
7835
   */
7836
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
7837
7838
  ada_really_inline void parse_path(std::string_view input);
7839
7840
  /**
7841
   * A URL cannot have a username/password/port if its host is null or the empty
7842
   * string, or its scheme is "file".
7843
   */
7844
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
7845
7846
  template <bool override_hostname = false>
7847
  bool set_host_or_hostname(std::string_view input);
7848
7849
  ada_really_inline bool parse_host(std::string_view input);
7850
7851
  inline void update_base_authority(std::string_view base_buffer,
7852
                                    const url_components& base);
7853
  inline void update_unencoded_base_hash(std::string_view input);
7854
  inline void update_base_hostname(std::string_view input);
7855
  inline void update_base_search(std::string_view input);
7856
  inline void update_base_search(std::string_view input,
7857
                                 const uint8_t* query_percent_encode_set);
7858
  inline void update_base_pathname(std::string_view input);
7859
  inline void update_base_username(std::string_view input);
7860
  inline void append_base_username(std::string_view input);
7861
  inline void update_base_password(std::string_view input);
7862
  inline void append_base_password(std::string_view input);
7863
  inline void update_base_port(uint32_t input);
7864
  inline void append_base_pathname(std::string_view input);
7865
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
7866
  constexpr void clear_hostname();
7867
  constexpr void clear_password();
7868
  constexpr void clear_pathname() override;
7869
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
7870
  void delete_dash_dot();
7871
  inline void consume_prepared_path(std::string_view input);
7872
  template <bool has_state_override = false>
7873
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
7874
      std::string_view input);
7875
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
7876
                                                std::string_view input);
7877
  [[nodiscard]] constexpr bool has_authority() const noexcept;
7878
  constexpr void set_protocol_as_file();
7879
  inline void set_scheme(std::string_view new_scheme);
7880
  /**
7881
   * Fast function to set the scheme from a view with a colon in the
7882
   * buffer, does not change type.
7883
   */
7884
  inline void set_scheme_from_view_with_colon(
7885
      std::string_view new_scheme_with_colon);
7886
  inline void copy_scheme(const url_aggregator& u);
7887
7888
  inline void update_host_to_base_host(const std::string_view input);
7889
7890
};  // url_aggregator
7891
7892
inline std::ostream& operator<<(std::ostream& out, const url& u);
7893
}  // namespace ada
7894
7895
#endif
7896
/* end file include/ada/url_aggregator.h */
7897
/* begin file include/ada/url_aggregator-inl.h */
7898
/**
7899
 * @file url_aggregator-inl.h
7900
 * @brief Inline functions for url aggregator
7901
 */
7902
#ifndef ADA_URL_AGGREGATOR_INL_H
7903
#define ADA_URL_AGGREGATOR_INL_H
7904
7905
/* begin file include/ada/unicode-inl.h */
7906
/**
7907
 * @file unicode-inl.h
7908
 * @brief Definitions for unicode operations.
7909
 */
7910
#ifndef ADA_UNICODE_INL_H
7911
#define ADA_UNICODE_INL_H
7912
7913
/**
7914
 * Unicode operations. These functions are not part of our public API and may
7915
 * change at any time.
7916
 *
7917
 * private
7918
 * @namespace ada::unicode
7919
 * @brief Includes the declarations for unicode operations
7920
 */
7921
namespace ada::unicode {
7922
ada_really_inline size_t percent_encode_index(const std::string_view input,
7923
2.45k
                                              const uint8_t character_set[]) {
7924
2.45k
  const char* data = input.data();
7925
2.45k
  const size_t size = input.size();
7926
7927
  // Process 8 bytes at a time using unrolled loop
7928
2.45k
  size_t i = 0;
7929
4.52k
  for (; i + 8 <= size; i += 8) {
7930
2.68k
    unsigned char chunk[8];
7931
2.68k
    std::memcpy(&chunk, data + i,
7932
2.68k
                8);  // entices compiler to unconditionally process 8 characters
7933
7934
    // Check 8 characters at once
7935
20.3k
    for (size_t j = 0; j < 8; j++) {
7936
18.2k
      if (character_sets::bit_at(character_set, chunk[j])) {
7937
622
        return i + j;
7938
622
      }
7939
18.2k
    }
7940
2.68k
  }
7941
7942
  // Handle remaining bytes
7943
4.69k
  for (; i < size; i++) {
7944
3.31k
    if (character_sets::bit_at(character_set, data[i])) {
7945
451
      return i;
7946
451
    }
7947
3.31k
  }
7948
7949
1.38k
  return size;
7950
1.83k
}
7951
}  // namespace ada::unicode
7952
7953
#endif  // ADA_UNICODE_INL_H
7954
/* end file include/ada/unicode-inl.h */
7955
7956
#include <charconv>
7957
#include <ostream>
7958
#include <string_view>
7959
7960
namespace ada {
7961
7962
inline void url_aggregator::update_base_authority(
7963
0
    std::string_view base_buffer, const ada::url_components& base) {
7964
0
  std::string_view input = base_buffer.substr(
7965
0
      base.protocol_end, base.host_start - base.protocol_end);
7966
0
  ada_log("url_aggregator::update_base_authority ", input);
7967
7968
0
  bool input_starts_with_dash = input.starts_with("//");
7969
0
  uint32_t diff = components.host_start - components.protocol_end;
7970
7971
0
  buffer.erase(components.protocol_end,
7972
0
               components.host_start - components.protocol_end);
7973
0
  components.username_end = components.protocol_end;
7974
7975
0
  if (input_starts_with_dash) {
7976
0
    input.remove_prefix(2);
7977
0
    diff += 2;  // add "//"
7978
0
    buffer.insert(components.protocol_end, "//");
7979
0
    components.username_end += 2;
7980
0
  }
7981
7982
0
  size_t password_delimiter = input.find(':');
7983
7984
  // Check if input contains both username and password by checking the
7985
  // delimiter: ":" A typical input that contains authority would be "user:pass"
7986
0
  if (password_delimiter != std::string_view::npos) {
7987
    // Insert both username and password
7988
0
    std::string_view username = input.substr(0, password_delimiter);
7989
0
    std::string_view password = input.substr(password_delimiter + 1);
7990
7991
0
    buffer.insert(components.protocol_end + diff, username);
7992
0
    diff += uint32_t(username.size());
7993
0
    buffer.insert(components.protocol_end + diff, ":");
7994
0
    components.username_end = components.protocol_end + diff;
7995
0
    buffer.insert(components.protocol_end + diff + 1, password);
7996
0
    diff += uint32_t(password.size()) + 1;
7997
0
  } else if (!input.empty()) {
7998
    // Insert only username
7999
0
    buffer.insert(components.protocol_end + diff, input);
8000
0
    components.username_end =
8001
0
        components.protocol_end + diff + uint32_t(input.size());
8002
0
    diff += uint32_t(input.size());
8003
0
  }
8004
8005
0
  components.host_start += diff;
8006
8007
0
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
8008
0
    buffer.insert(components.host_start, "@");
8009
0
    diff++;
8010
0
  }
8011
0
  components.host_end += diff;
8012
0
  components.pathname_start += diff;
8013
0
  if (components.search_start != url_components::omitted) {
8014
0
    components.search_start += diff;
8015
0
  }
8016
0
  if (components.hash_start != url_components::omitted) {
8017
0
    components.hash_start += diff;
8018
0
  }
8019
0
}
8020
8021
51
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
8022
51
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
8023
51
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
8024
51
          " bytes] components.hash_start = ", components.hash_start);
8025
51
  ADA_ASSERT_TRUE(validate());
8026
51
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8027
51
  if (components.hash_start != url_components::omitted) {
8028
0
    buffer.resize(components.hash_start);
8029
0
  }
8030
51
  components.hash_start = uint32_t(buffer.size());
8031
51
  buffer += "#";
8032
51
  bool encoding_required = unicode::percent_encode<true>(
8033
51
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
8034
  // When encoding_required is false, then buffer is left unchanged, and percent
8035
  // encoding was not deemed required.
8036
51
  if (!encoding_required) {
8037
30
    buffer.append(input);
8038
30
  }
8039
51
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
8040
51
          buffer, "' [", buffer.size(), " bytes]");
8041
51
  ADA_ASSERT_TRUE(validate());
8042
51
}
8043
8044
ada_really_inline uint32_t url_aggregator::replace_and_resize(
8045
1.37k
    uint32_t start, uint32_t end, std::string_view input) {
8046
1.37k
  uint32_t current_length = end - start;
8047
1.37k
  uint32_t input_size = uint32_t(input.size());
8048
1.37k
  uint32_t new_difference = input_size - current_length;
8049
8050
1.37k
  if (current_length == 0) {
8051
1.18k
    buffer.insert(start, input);
8052
1.18k
  } else if (input_size == current_length) {
8053
7
    buffer.replace(start, input_size, input);
8054
183
  } else if (input_size < current_length) {
8055
41
    buffer.erase(start, current_length - input_size);
8056
41
    buffer.replace(start, input_size, input);
8057
142
  } else {
8058
142
    buffer.replace(start, current_length, input.substr(0, current_length));
8059
142
    buffer.insert(start + current_length, input.substr(current_length));
8060
142
  }
8061
8062
1.37k
  return new_difference;
8063
1.37k
}
8064
8065
1.09k
inline void url_aggregator::update_base_hostname(const std::string_view input) {
8066
1.09k
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
8067
1.09k
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
8068
1.09k
  ADA_ASSERT_TRUE(validate());
8069
1.09k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8070
8071
  // This next line is required for when parsing a URL like `foo://`
8072
1.09k
  add_authority_slashes_if_needed();
8073
8074
1.09k
  bool has_credentials = components.protocol_end + 2 < components.host_start;
8075
1.09k
  uint32_t new_difference =
8076
1.09k
      replace_and_resize(components.host_start, components.host_end, input);
8077
8078
1.09k
  if (has_credentials) {
8079
65
    buffer.insert(components.host_start, "@");
8080
65
    new_difference++;
8081
65
  }
8082
1.09k
  components.host_end += new_difference;
8083
1.09k
  components.pathname_start += new_difference;
8084
1.09k
  if (components.search_start != url_components::omitted) {
8085
0
    components.search_start += new_difference;
8086
0
  }
8087
1.09k
  if (components.hash_start != url_components::omitted) {
8088
0
    components.hash_start += new_difference;
8089
0
  }
8090
1.09k
  ADA_ASSERT_TRUE(validate());
8091
1.09k
}
8092
8093
[[nodiscard]] ada_really_inline uint32_t
8094
277
url_aggregator::get_pathname_length() const noexcept {
8095
277
  ada_log("url_aggregator::get_pathname_length");
8096
277
  uint32_t ending_index = uint32_t(buffer.size());
8097
277
  if (components.search_start != url_components::omitted) {
8098
0
    ending_index = components.search_start;
8099
277
  } else if (components.hash_start != url_components::omitted) {
8100
0
    ending_index = components.hash_start;
8101
0
  }
8102
277
  return ending_index - components.pathname_start;
8103
277
}
8104
8105
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
8106
299
    const noexcept {
8107
299
  return buffer.size() == components.pathname_start;
8108
299
}
8109
8110
0
inline void url_aggregator::update_base_search(std::string_view input) {
8111
0
  ada_log("url_aggregator::update_base_search ", input);
8112
0
  ADA_ASSERT_TRUE(validate());
8113
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8114
0
  if (input.empty()) {
8115
0
    clear_search();
8116
0
    return;
8117
0
  }
8118
8119
0
  if (input[0] == '?') {
8120
0
    input.remove_prefix(1);
8121
0
  }
8122
8123
0
  if (components.hash_start == url_components::omitted) {
8124
0
    if (components.search_start == url_components::omitted) {
8125
0
      components.search_start = uint32_t(buffer.size());
8126
0
      buffer += "?";
8127
0
    } else {
8128
0
      buffer.resize(components.search_start + 1);
8129
0
    }
8130
8131
0
    buffer.append(input);
8132
0
  } else {
8133
0
    if (components.search_start == url_components::omitted) {
8134
0
      components.search_start = components.hash_start;
8135
0
    } else {
8136
0
      buffer.erase(components.search_start,
8137
0
                   components.hash_start - components.search_start);
8138
0
      components.hash_start = components.search_start;
8139
0
    }
8140
8141
0
    buffer.insert(components.search_start, "?");
8142
0
    buffer.insert(components.search_start + 1, input);
8143
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8144
0
  }
8145
8146
0
  ADA_ASSERT_TRUE(validate());
8147
0
}
8148
8149
inline void url_aggregator::update_base_search(
8150
212
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8151
212
  ada_log("url_aggregator::update_base_search ", input,
8152
212
          " with encoding parameter ", to_string(), "\n", to_diagram());
8153
212
  ADA_ASSERT_TRUE(validate());
8154
212
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8155
8156
212
  if (components.hash_start == url_components::omitted) {
8157
212
    if (components.search_start == url_components::omitted) {
8158
212
      components.search_start = uint32_t(buffer.size());
8159
212
      buffer += "?";
8160
212
    } else {
8161
0
      buffer.resize(components.search_start + 1);
8162
0
    }
8163
8164
212
    bool encoding_required =
8165
212
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8166
    // When encoding_required is false, then buffer is left unchanged, and
8167
    // percent encoding was not deemed required.
8168
212
    if (!encoding_required) {
8169
191
      buffer.append(input);
8170
191
    }
8171
212
  } else {
8172
0
    if (components.search_start == url_components::omitted) {
8173
0
      components.search_start = components.hash_start;
8174
0
    } else {
8175
0
      buffer.erase(components.search_start,
8176
0
                   components.hash_start - components.search_start);
8177
0
      components.hash_start = components.search_start;
8178
0
    }
8179
8180
0
    buffer.insert(components.search_start, "?");
8181
0
    size_t idx =
8182
0
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8183
0
    if (idx == input.size()) {
8184
0
      buffer.insert(components.search_start + 1, input);
8185
0
      components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8186
0
    } else {
8187
0
      buffer.insert(components.search_start + 1, input, 0, idx);
8188
0
      input.remove_prefix(idx);
8189
      // We only create a temporary string if we need percent encoding and
8190
      // we attempt to create as small a temporary string as we can.
8191
0
      std::string encoded =
8192
0
          ada::unicode::percent_encode(input, query_percent_encode_set);
8193
0
      buffer.insert(components.search_start + idx + 1, encoded);
8194
0
      components.hash_start +=
8195
0
          uint32_t(encoded.size() + idx + 1);  // Do not forget `?`
8196
0
    }
8197
0
  }
8198
8199
212
  ADA_ASSERT_TRUE(validate());
8200
212
}
8201
8202
277
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8203
277
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8204
277
          " bytes] \n", to_diagram());
8205
277
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8206
277
  ADA_ASSERT_TRUE(validate());
8207
8208
277
  const bool begins_with_dashdash = input.starts_with("//");
8209
277
  if (!begins_with_dashdash && has_dash_dot()) {
8210
    // We must delete the ./
8211
0
    delete_dash_dot();
8212
0
  }
8213
8214
277
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8215
0
      !has_dash_dot()) {
8216
    // If url's host is null, url does not have an opaque path, url's path's
8217
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8218
    // output.
8219
0
    buffer.insert(components.pathname_start, "/.");
8220
0
    components.pathname_start += 2;
8221
0
    if (components.search_start != url_components::omitted) {
8222
0
      components.search_start += 2;
8223
0
    }
8224
0
    if (components.hash_start != url_components::omitted) {
8225
0
      components.hash_start += 2;
8226
0
    }
8227
0
  }
8228
8229
277
  uint32_t difference = replace_and_resize(
8230
277
      components.pathname_start,
8231
277
      components.pathname_start + get_pathname_length(), input);
8232
277
  if (components.search_start != url_components::omitted) {
8233
0
    components.search_start += difference;
8234
0
  }
8235
277
  if (components.hash_start != url_components::omitted) {
8236
0
    components.hash_start += difference;
8237
0
  }
8238
277
  ADA_ASSERT_TRUE(validate());
8239
277
}
8240
8241
0
inline void url_aggregator::append_base_pathname(const std::string_view input) {
8242
0
  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
8243
0
          "\n", to_diagram());
8244
0
  ADA_ASSERT_TRUE(validate());
8245
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8246
#if ADA_DEVELOPMENT_CHECKS
8247
  // computing the expected password.
8248
  std::string path_expected(get_pathname());
8249
  path_expected.append(input);
8250
#endif  // ADA_DEVELOPMENT_CHECKS
8251
0
  uint32_t ending_index = uint32_t(buffer.size());
8252
0
  if (components.search_start != url_components::omitted) {
8253
0
    ending_index = components.search_start;
8254
0
  } else if (components.hash_start != url_components::omitted) {
8255
0
    ending_index = components.hash_start;
8256
0
  }
8257
0
  buffer.insert(ending_index, input);
8258
8259
0
  if (components.search_start != url_components::omitted) {
8260
0
    components.search_start += uint32_t(input.size());
8261
0
  }
8262
0
  if (components.hash_start != url_components::omitted) {
8263
0
    components.hash_start += uint32_t(input.size());
8264
0
  }
8265
#if ADA_DEVELOPMENT_CHECKS
8266
  std::string path_after = std::string(get_pathname());
8267
  ADA_ASSERT_EQUAL(
8268
      path_expected, path_after,
8269
      "append_base_pathname problem after inserting " + std::string(input));
8270
#endif  // ADA_DEVELOPMENT_CHECKS
8271
0
  ADA_ASSERT_TRUE(validate());
8272
0
}
8273
8274
0
inline void url_aggregator::update_base_username(const std::string_view input) {
8275
0
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8276
0
          "\n", to_diagram());
8277
0
  ADA_ASSERT_TRUE(validate());
8278
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8279
8280
0
  add_authority_slashes_if_needed();
8281
8282
0
  bool has_password = has_non_empty_password();
8283
0
  bool host_starts_with_at = buffer.size() > components.host_start &&
8284
0
                             buffer[components.host_start] == '@';
8285
0
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8286
0
                                     components.username_end, input);
8287
8288
0
  components.username_end += diff;
8289
0
  components.host_start += diff;
8290
8291
0
  if (!input.empty() && !host_starts_with_at) {
8292
0
    buffer.insert(components.host_start, "@");
8293
0
    diff++;
8294
0
  } else if (input.empty() && host_starts_with_at && !has_password) {
8295
    // Input is empty, there is no password, and we need to remove "@" from
8296
    // hostname
8297
0
    buffer.erase(components.host_start, 1);
8298
0
    diff--;
8299
0
  }
8300
8301
0
  components.host_end += diff;
8302
0
  components.pathname_start += diff;
8303
0
  if (components.search_start != url_components::omitted) {
8304
0
    components.search_start += diff;
8305
0
  }
8306
0
  if (components.hash_start != url_components::omitted) {
8307
0
    components.hash_start += diff;
8308
0
  }
8309
0
  ADA_ASSERT_TRUE(validate());
8310
0
}
8311
8312
678
inline void url_aggregator::append_base_username(const std::string_view input) {
8313
678
  ada_log("url_aggregator::append_base_username ", input);
8314
678
  ADA_ASSERT_TRUE(validate());
8315
678
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8316
#if ADA_DEVELOPMENT_CHECKS
8317
  // computing the expected password.
8318
  std::string username_expected(get_username());
8319
  username_expected.append(input);
8320
#endif  // ADA_DEVELOPMENT_CHECKS
8321
678
  add_authority_slashes_if_needed();
8322
8323
  // If input is empty, do nothing.
8324
678
  if (input.empty()) {
8325
251
    return;
8326
251
  }
8327
8328
427
  uint32_t difference = uint32_t(input.size());
8329
427
  buffer.insert(components.username_end, input);
8330
427
  components.username_end += difference;
8331
427
  components.host_start += difference;
8332
8333
427
  if (buffer[components.host_start] != '@' &&
8334
114
      components.host_start != components.host_end) {
8335
114
    buffer.insert(components.host_start, "@");
8336
114
    difference++;
8337
114
  }
8338
8339
427
  components.host_end += difference;
8340
427
  components.pathname_start += difference;
8341
427
  if (components.search_start != url_components::omitted) {
8342
0
    components.search_start += difference;
8343
0
  }
8344
427
  if (components.hash_start != url_components::omitted) {
8345
0
    components.hash_start += difference;
8346
0
  }
8347
#if ADA_DEVELOPMENT_CHECKS
8348
  std::string username_after(get_username());
8349
  ADA_ASSERT_EQUAL(
8350
      username_expected, username_after,
8351
      "append_base_username problem after inserting " + std::string(input));
8352
#endif  // ADA_DEVELOPMENT_CHECKS
8353
427
  ADA_ASSERT_TRUE(validate());
8354
427
}
8355
8356
0
constexpr void url_aggregator::clear_password() {
8357
0
  ada_log("url_aggregator::clear_password ", to_string());
8358
0
  ADA_ASSERT_TRUE(validate());
8359
0
  if (!has_password()) {
8360
0
    return;
8361
0
  }
8362
8363
0
  uint32_t diff = components.host_start - components.username_end;
8364
0
  buffer.erase(components.username_end, diff);
8365
0
  components.host_start -= diff;
8366
0
  components.host_end -= diff;
8367
0
  components.pathname_start -= diff;
8368
0
  if (components.search_start != url_components::omitted) {
8369
0
    components.search_start -= diff;
8370
0
  }
8371
0
  if (components.hash_start != url_components::omitted) {
8372
0
    components.hash_start -= diff;
8373
0
  }
8374
0
}
8375
8376
0
inline void url_aggregator::update_base_password(const std::string_view input) {
8377
0
  ada_log("url_aggregator::update_base_password ", input);
8378
0
  ADA_ASSERT_TRUE(validate());
8379
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8380
8381
0
  add_authority_slashes_if_needed();
8382
8383
  // TODO: Optimization opportunity. Merge the following removal functions.
8384
0
  if (input.empty()) {
8385
0
    clear_password();
8386
8387
    // Remove username too, if it is empty.
8388
0
    if (!has_non_empty_username()) {
8389
0
      update_base_username("");
8390
0
    }
8391
8392
0
    return;
8393
0
  }
8394
8395
0
  bool password_exists = has_password();
8396
0
  uint32_t difference = uint32_t(input.size());
8397
8398
0
  if (password_exists) {
8399
0
    uint32_t current_length =
8400
0
        components.host_start - components.username_end - 1;
8401
0
    buffer.erase(components.username_end + 1, current_length);
8402
0
    difference -= current_length;
8403
0
  } else {
8404
0
    buffer.insert(components.username_end, ":");
8405
0
    difference++;
8406
0
  }
8407
8408
0
  buffer.insert(components.username_end + 1, input);
8409
0
  components.host_start += difference;
8410
8411
  // The following line is required to add "@" to hostname. When updating
8412
  // password if hostname does not start with "@", it is "update_base_password"s
8413
  // responsibility to set it.
8414
0
  if (buffer[components.host_start] != '@') {
8415
0
    buffer.insert(components.host_start, "@");
8416
0
    difference++;
8417
0
  }
8418
8419
0
  components.host_end += difference;
8420
0
  components.pathname_start += difference;
8421
0
  if (components.search_start != url_components::omitted) {
8422
0
    components.search_start += difference;
8423
0
  }
8424
0
  if (components.hash_start != url_components::omitted) {
8425
0
    components.hash_start += difference;
8426
0
  }
8427
0
  ADA_ASSERT_TRUE(validate());
8428
0
}
8429
8430
225
inline void url_aggregator::append_base_password(const std::string_view input) {
8431
225
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8432
225
          "\n", to_diagram());
8433
225
  ADA_ASSERT_TRUE(validate());
8434
225
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8435
#if ADA_DEVELOPMENT_CHECKS
8436
  // computing the expected password.
8437
  std::string password_expected = std::string(get_password());
8438
  password_expected.append(input);
8439
#endif  // ADA_DEVELOPMENT_CHECKS
8440
225
  add_authority_slashes_if_needed();
8441
8442
  // If input is empty, do nothing.
8443
225
  if (input.empty()) {
8444
80
    return;
8445
80
  }
8446
8447
145
  uint32_t difference = uint32_t(input.size());
8448
145
  if (has_password()) {
8449
104
    buffer.insert(components.host_start, input);
8450
104
  } else {
8451
41
    difference++;  // Increment for ":"
8452
41
    buffer.insert(components.username_end, ":");
8453
41
    buffer.insert(components.username_end + 1, input);
8454
41
  }
8455
145
  components.host_start += difference;
8456
8457
  // The following line is required to add "@" to hostname. When updating
8458
  // password if hostname does not start with "@", it is "append_base_password"s
8459
  // responsibility to set it.
8460
145
  if (buffer[components.host_start] != '@') {
8461
7
    buffer.insert(components.host_start, "@");
8462
7
    difference++;
8463
7
  }
8464
8465
145
  components.host_end += difference;
8466
145
  components.pathname_start += difference;
8467
145
  if (components.search_start != url_components::omitted) {
8468
0
    components.search_start += difference;
8469
0
  }
8470
145
  if (components.hash_start != url_components::omitted) {
8471
0
    components.hash_start += difference;
8472
0
  }
8473
#if ADA_DEVELOPMENT_CHECKS
8474
  std::string password_after(get_password());
8475
  ADA_ASSERT_EQUAL(
8476
      password_expected, password_after,
8477
      "append_base_password problem after inserting " + std::string(input));
8478
#endif  // ADA_DEVELOPMENT_CHECKS
8479
145
  ADA_ASSERT_TRUE(validate());
8480
145
}
8481
8482
24
inline void url_aggregator::update_base_port(uint32_t input) {
8483
24
  ada_log("url_aggregator::update_base_port");
8484
24
  ADA_ASSERT_TRUE(validate());
8485
24
  if (input == url_components::omitted) {
8486
0
    clear_port();
8487
0
    return;
8488
0
  }
8489
  // calling std::to_string(input.value()) is unfortunate given that the port
8490
  // value is probably already available as a string.
8491
24
  std::string value = helpers::concat(":", std::to_string(input));
8492
24
  uint32_t difference = uint32_t(value.size());
8493
8494
24
  if (components.port != url_components::omitted) {
8495
0
    difference -= components.pathname_start - components.host_end;
8496
0
    buffer.erase(components.host_end,
8497
0
                 components.pathname_start - components.host_end);
8498
0
  }
8499
8500
24
  buffer.insert(components.host_end, value);
8501
24
  components.pathname_start += difference;
8502
24
  if (components.search_start != url_components::omitted) {
8503
0
    components.search_start += difference;
8504
0
  }
8505
24
  if (components.hash_start != url_components::omitted) {
8506
0
    components.hash_start += difference;
8507
0
  }
8508
24
  components.port = input;
8509
24
  ADA_ASSERT_TRUE(validate());
8510
24
}
8511
8512
34
inline void url_aggregator::clear_port() {
8513
34
  ada_log("url_aggregator::clear_port");
8514
34
  ADA_ASSERT_TRUE(validate());
8515
34
  if (components.port == url_components::omitted) {
8516
34
    return;
8517
34
  }
8518
0
  uint32_t length = components.pathname_start - components.host_end;
8519
0
  buffer.erase(components.host_end, length);
8520
0
  components.pathname_start -= length;
8521
0
  if (components.search_start != url_components::omitted) {
8522
0
    components.search_start -= length;
8523
0
  }
8524
0
  if (components.hash_start != url_components::omitted) {
8525
0
    components.hash_start -= length;
8526
0
  }
8527
0
  components.port = url_components::omitted;
8528
0
  ADA_ASSERT_TRUE(validate());
8529
0
}
8530
8531
0
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8532
0
  ada_log("url_aggregator::retrieve_base_port");
8533
0
  return components.port;
8534
0
}
8535
8536
0
inline void url_aggregator::clear_search() {
8537
0
  ada_log("url_aggregator::clear_search");
8538
0
  ADA_ASSERT_TRUE(validate());
8539
0
  if (components.search_start == url_components::omitted) {
8540
0
    return;
8541
0
  }
8542
8543
0
  if (components.hash_start == url_components::omitted) {
8544
0
    buffer.resize(components.search_start);
8545
0
  } else {
8546
0
    buffer.erase(components.search_start,
8547
0
                 components.hash_start - components.search_start);
8548
0
    components.hash_start = components.search_start;
8549
0
  }
8550
8551
0
  components.search_start = url_components::omitted;
8552
8553
#if ADA_DEVELOPMENT_CHECKS
8554
  ADA_ASSERT_EQUAL(get_search(), "",
8555
                   "search should have been cleared on buffer=" + buffer +
8556
                       " with " + components.to_string() + "\n" + to_diagram());
8557
#endif
8558
0
  ADA_ASSERT_TRUE(validate());
8559
0
}
8560
8561
0
inline void url_aggregator::clear_hash() {
8562
0
  ada_log("url_aggregator::clear_hash");
8563
0
  ADA_ASSERT_TRUE(validate());
8564
0
  if (components.hash_start == url_components::omitted) {
8565
0
    return;
8566
0
  }
8567
0
  buffer.resize(components.hash_start);
8568
0
  components.hash_start = url_components::omitted;
8569
8570
#if ADA_DEVELOPMENT_CHECKS
8571
  ADA_ASSERT_EQUAL(get_hash(), "",
8572
                   "hash should have been cleared on buffer=" + buffer +
8573
                       " with " + components.to_string() + "\n" + to_diagram());
8574
#endif
8575
0
  ADA_ASSERT_TRUE(validate());
8576
0
}
8577
8578
0
constexpr void url_aggregator::clear_pathname() {
8579
0
  ada_log("url_aggregator::clear_pathname");
8580
0
  ADA_ASSERT_TRUE(validate());
8581
0
  uint32_t ending_index = uint32_t(buffer.size());
8582
0
  if (components.search_start != url_components::omitted) {
8583
0
    ending_index = components.search_start;
8584
0
  } else if (components.hash_start != url_components::omitted) {
8585
0
    ending_index = components.hash_start;
8586
0
  }
8587
0
  uint32_t pathname_length = ending_index - components.pathname_start;
8588
0
  buffer.erase(components.pathname_start, pathname_length);
8589
0
  uint32_t difference = pathname_length;
8590
0
  if (components.pathname_start == components.host_end + 2 &&
8591
0
      buffer[components.host_end] == '/' &&
8592
0
      buffer[components.host_end + 1] == '.') {
8593
0
    components.pathname_start -= 2;
8594
0
    buffer.erase(components.host_end, 2);
8595
0
    difference += 2;
8596
0
  }
8597
0
  if (components.search_start != url_components::omitted) {
8598
0
    components.search_start -= difference;
8599
0
  }
8600
0
  if (components.hash_start != url_components::omitted) {
8601
0
    components.hash_start -= difference;
8602
0
  }
8603
0
  ada_log("url_aggregator::clear_pathname completed, running checks...");
8604
#if ADA_DEVELOPMENT_CHECKS
8605
  ADA_ASSERT_EQUAL(get_pathname(), "",
8606
                   "pathname should have been cleared on buffer=" + buffer +
8607
                       " with " + components.to_string() + "\n" + to_diagram());
8608
#endif
8609
0
  ADA_ASSERT_TRUE(validate());
8610
0
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
8611
0
}
8612
8613
0
constexpr void url_aggregator::clear_hostname() {
8614
0
  ada_log("url_aggregator::clear_hostname");
8615
0
  ADA_ASSERT_TRUE(validate());
8616
0
  if (!has_authority()) {
8617
0
    return;
8618
0
  }
8619
0
  ADA_ASSERT_TRUE(has_authority());
8620
8621
0
  uint32_t hostname_length = components.host_end - components.host_start;
8622
0
  uint32_t start = components.host_start;
8623
8624
  // If hostname starts with "@", we should not remove that character.
8625
0
  if (hostname_length > 0 && buffer[start] == '@') {
8626
0
    start++;
8627
0
    hostname_length--;
8628
0
  }
8629
0
  buffer.erase(start, hostname_length);
8630
0
  components.host_end = start;
8631
0
  components.pathname_start -= hostname_length;
8632
0
  if (components.search_start != url_components::omitted) {
8633
0
    components.search_start -= hostname_length;
8634
0
  }
8635
0
  if (components.hash_start != url_components::omitted) {
8636
0
    components.hash_start -= hostname_length;
8637
0
  }
8638
#if ADA_DEVELOPMENT_CHECKS
8639
  ADA_ASSERT_EQUAL(get_hostname(), "",
8640
                   "hostname should have been cleared on buffer=" + buffer +
8641
                       " with " + components.to_string() + "\n" + to_diagram());
8642
#endif
8643
0
  ADA_ASSERT_TRUE(has_authority());
8644
0
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
8645
0
                   "hostname should have been cleared on buffer=" + buffer +
8646
0
                       " with " + components.to_string() + "\n" + to_diagram());
8647
0
  ADA_ASSERT_TRUE(validate());
8648
0
}
8649
8650
0
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
8651
0
  ada_log("url_aggregator::has_hash");
8652
0
  return components.hash_start != url_components::omitted;
8653
0
}
8654
8655
0
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
8656
0
  ada_log("url_aggregator::has_search");
8657
0
  return components.search_start != url_components::omitted;
8658
0
}
8659
8660
0
constexpr bool url_aggregator::has_credentials() const noexcept {
8661
0
  ada_log("url_aggregator::has_credentials");
8662
0
  return has_non_empty_username() || has_non_empty_password();
8663
0
}
8664
8665
0
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
8666
0
  ada_log("url_aggregator::cannot_have_credentials_or_port");
8667
0
  return type == ada::scheme::type::FILE ||
8668
0
         components.host_start == components.host_end;
8669
0
}
8670
8671
[[nodiscard]] ada_really_inline const ada::url_components&
8672
0
url_aggregator::get_components() const noexcept {
8673
0
  return components;
8674
0
}
8675
8676
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
8677
2.14k
    const noexcept {
8678
2.14k
  ada_log("url_aggregator::has_authority");
8679
  // Performance: instead of doing this potentially expensive check, we could
8680
  // have a boolean in the struct.
8681
2.14k
  return components.protocol_end + 2 <= components.host_start &&
8682
1.11k
         buffer[components.protocol_end] == '/' &&
8683
1.11k
         buffer[components.protocol_end + 1] == '/';
8684
2.14k
}
8685
8686
1.99k
inline void ada::url_aggregator::add_authority_slashes_if_needed() {
8687
1.99k
  ada_log("url_aggregator::add_authority_slashes_if_needed");
8688
1.99k
  ADA_ASSERT_TRUE(validate());
8689
  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
8690
  // to insert
8691
  // `//` initially to the buffer, since it depends on the hostname existence.
8692
1.99k
  if (has_authority()) {
8693
969
    return;
8694
969
  }
8695
  // Performance: the common case is components.protocol_end == buffer.size()
8696
  // Optimization opportunity: in many cases, the "//" is part of the input and
8697
  // the insert could be fused with another insert.
8698
1.02k
  buffer.insert(components.protocol_end, "//");
8699
1.02k
  components.username_end += 2;
8700
1.02k
  components.host_start += 2;
8701
1.02k
  components.host_end += 2;
8702
1.02k
  components.pathname_start += 2;
8703
1.02k
  if (components.search_start != url_components::omitted) {
8704
0
    components.search_start += 2;
8705
0
  }
8706
1.02k
  if (components.hash_start != url_components::omitted) {
8707
0
    components.hash_start += 2;
8708
0
  }
8709
1.02k
  ADA_ASSERT_TRUE(validate());
8710
1.02k
}
8711
8712
4.14k
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
8713
4.14k
  buffer.reserve(capacity);
8714
4.14k
}
8715
8716
0
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
8717
0
  ada_log("url_aggregator::has_non_empty_username");
8718
0
  return components.protocol_end + 2 < components.username_end;
8719
0
}
8720
8721
0
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
8722
0
  ada_log("url_aggregator::has_non_empty_password");
8723
0
  return components.host_start > components.username_end;
8724
0
}
8725
8726
145
constexpr bool url_aggregator::has_password() const noexcept {
8727
145
  ada_log("url_aggregator::has_password");
8728
  // This function does not care about the length of the password
8729
145
  return components.host_start > components.username_end &&
8730
104
         buffer[components.username_end] == ':';
8731
145
}
8732
8733
0
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
8734
0
  if (!has_hostname()) {
8735
0
    return false;
8736
0
  }
8737
0
  if (components.host_start == components.host_end) {
8738
0
    return true;
8739
0
  }
8740
0
  if (components.host_end > components.host_start + 1) {
8741
0
    return false;
8742
0
  }
8743
0
  return components.username_end != components.host_start;
8744
0
}
8745
8746
0
constexpr bool url_aggregator::has_hostname() const noexcept {
8747
0
  return has_authority();
8748
0
}
8749
8750
0
constexpr bool url_aggregator::has_port() const noexcept {
8751
0
  ada_log("url_aggregator::has_port");
8752
  // A URL cannot have a username/password/port if its host is null or the empty
8753
  // string, or its scheme is "file".
8754
0
  return has_hostname() && components.pathname_start != components.host_end;
8755
0
}
8756
8757
129
[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
8758
  // If url's host is null, url does not have an opaque path, url's path's size
8759
  // is greater than 1, and url's path[0] is the empty string, then append
8760
  // U+002F (/) followed by U+002E (.) to output.
8761
129
  ada_log("url_aggregator::has_dash_dot");
8762
#if ADA_DEVELOPMENT_CHECKS
8763
  // If pathname_start and host_end are exactly two characters apart, then we
8764
  // either have a one-digit port such as http://test.com:5?param=1 or else we
8765
  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
8766
  if (components.pathname_start == components.host_end + 2) {
8767
    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
8768
                     buffer[components.host_end + 1] == '.') ||
8769
                    (buffer[components.host_end] == ':' &&
8770
                     checkers::is_digit(buffer[components.host_end + 1])));
8771
  }
8772
  if (components.pathname_start == components.host_end + 2 &&
8773
      buffer[components.host_end] == '/' &&
8774
      buffer[components.host_end + 1] == '.') {
8775
    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
8776
    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
8777
    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
8778
  }
8779
#endif
8780
  // Performance: it should be uncommon for components.pathname_start ==
8781
  // components.host_end + 2 to be true. So we put this check first in the
8782
  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
8783
  // more expensive, but should be uncommon.
8784
129
  return components.pathname_start == components.host_end + 2 &&
8785
9
         !has_opaque_path && buffer[components.host_end] == '/' &&
8786
0
         buffer[components.host_end + 1] == '.';
8787
129
}
8788
8789
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
8790
0
    const noexcept ada_lifetime_bound {
8791
0
  ada_log("url_aggregator::get_href");
8792
0
  return buffer;
8793
0
}
8794
8795
0
[[nodiscard]] constexpr size_t url_aggregator::get_href_size() const noexcept {
8796
0
  return buffer.size();
8797
0
}
8798
8799
ada_really_inline size_t
8800
266
url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
8801
266
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
8802
266
  if (!view.empty() && view[0] == '-') {
8803
2
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
8804
2
    is_valid = false;
8805
2
    return 0;
8806
2
  }
8807
264
  uint16_t parsed_port{};
8808
264
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
8809
264
  if (r.ec == std::errc::result_out_of_range) {
8810
11
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
8811
11
    is_valid = false;
8812
11
    return 0;
8813
11
  }
8814
253
  ada_log("parse_port: ", parsed_port);
8815
253
  const size_t consumed = size_t(r.ptr - view.data());
8816
253
  ada_log("parse_port: consumed ", consumed);
8817
253
  if (check_trailing_content) {
8818
253
    is_valid &=
8819
253
        (consumed == view.size() || view[consumed] == '/' ||
8820
202
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
8821
253
  }
8822
253
  ada_log("parse_port: is_valid = ", is_valid);
8823
253
  if (is_valid) {
8824
58
    ada_log("parse_port", r.ec == std::errc());
8825
    // scheme_default_port can return 0, and we should allow 0 as a base port.
8826
58
    auto default_port = scheme_default_port();
8827
58
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
8828
58
                         (default_port != parsed_port);
8829
58
    if (r.ec == std::errc() && is_port_valid) {
8830
24
      update_base_port(parsed_port);
8831
34
    } else {
8832
34
      clear_port();
8833
34
    }
8834
58
  }
8835
253
  return consumed;
8836
264
}
8837
8838
0
constexpr void url_aggregator::set_protocol_as_file() {
8839
0
  ada_log("url_aggregator::set_protocol_as_file ");
8840
0
  ADA_ASSERT_TRUE(validate());
8841
0
  type = ada::scheme::type::FILE;
8842
  // next line could overflow but unsigned arithmetic has well-defined
8843
  // overflows.
8844
0
  uint32_t new_difference = 5 - components.protocol_end;
8845
8846
0
  if (buffer.empty()) {
8847
0
    buffer.append("file:");
8848
0
  } else {
8849
0
    buffer.erase(0, components.protocol_end);
8850
0
    buffer.insert(0, "file:");
8851
0
  }
8852
0
  components.protocol_end = 5;
8853
8854
  // Update the rest of the components.
8855
0
  components.username_end += new_difference;
8856
0
  components.host_start += new_difference;
8857
0
  components.host_end += new_difference;
8858
0
  components.pathname_start += new_difference;
8859
0
  if (components.search_start != url_components::omitted) {
8860
0
    components.search_start += new_difference;
8861
0
  }
8862
0
  if (components.hash_start != url_components::omitted) {
8863
0
    components.hash_start += new_difference;
8864
0
  }
8865
0
  ADA_ASSERT_TRUE(validate());
8866
0
}
8867
8868
576
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
8869
576
  if (!is_valid) {
8870
0
    return true;
8871
0
  }
8872
576
  if (!components.check_offset_consistency()) {
8873
0
    ada_log("url_aggregator::validate inconsistent components \n",
8874
0
            to_diagram());
8875
0
    return false;
8876
0
  }
8877
  // We have a credible components struct, but let us investivate more
8878
  // carefully:
8879
  /**
8880
   * https://user:pass@example.com:1234/foo/bar?baz#quux
8881
   *       |     |    |          | ^^^^|       |   |
8882
   *       |     |    |          | |   |       |   `----- hash_start
8883
   *       |     |    |          | |   |       `--------- search_start
8884
   *       |     |    |          | |   `----------------- pathname_start
8885
   *       |     |    |          | `--------------------- port
8886
   *       |     |    |          `----------------------- host_end
8887
   *       |     |    `---------------------------------- host_start
8888
   *       |     `--------------------------------------- username_end
8889
   *       `--------------------------------------------- protocol_end
8890
   */
8891
576
  if (components.protocol_end == url_components::omitted) {
8892
0
    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
8893
0
    return false;
8894
0
  }
8895
576
  if (components.username_end == url_components::omitted) {
8896
0
    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
8897
0
    return false;
8898
0
  }
8899
576
  if (components.host_start == url_components::omitted) {
8900
0
    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
8901
0
    return false;
8902
0
  }
8903
576
  if (components.host_end == url_components::omitted) {
8904
0
    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
8905
0
    return false;
8906
0
  }
8907
576
  if (components.pathname_start == url_components::omitted) {
8908
0
    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
8909
0
    return false;
8910
0
  }
8911
8912
576
  if (components.protocol_end > buffer.size()) {
8913
0
    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
8914
0
    return false;
8915
0
  }
8916
576
  if (components.username_end > buffer.size()) {
8917
0
    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
8918
0
    return false;
8919
0
  }
8920
576
  if (components.host_start > buffer.size()) {
8921
0
    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
8922
0
    return false;
8923
0
  }
8924
576
  if (components.host_end > buffer.size()) {
8925
0
    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
8926
0
    return false;
8927
0
  }
8928
576
  if (components.pathname_start > buffer.size()) {
8929
0
    ada_log("url_aggregator::validate pathname_start overflow \n",
8930
0
            to_diagram());
8931
0
    return false;
8932
0
  }
8933
8934
576
  if (components.protocol_end > 0) {
8935
576
    if (buffer[components.protocol_end - 1] != ':') {
8936
0
      ada_log(
8937
0
          "url_aggregator::validate missing : at the end of the protocol \n",
8938
0
          to_diagram());
8939
0
      return false;
8940
0
    }
8941
576
  }
8942
8943
576
  if (components.username_end != buffer.size() &&
8944
576
      components.username_end > components.protocol_end + 2) {
8945
33
    if (buffer[components.username_end] != ':' &&
8946
20
        buffer[components.username_end] != '@') {
8947
0
      ada_log(
8948
0
          "url_aggregator::validate missing : or @ at the end of the username "
8949
0
          "\n",
8950
0
          to_diagram());
8951
0
      return false;
8952
0
    }
8953
33
  }
8954
8955
576
  if (components.host_start != buffer.size()) {
8956
576
    if (components.host_start > components.username_end) {
8957
13
      if (buffer[components.host_start] != '@') {
8958
0
        ada_log(
8959
0
            "url_aggregator::validate missing @ at the end of the password \n",
8960
0
            to_diagram());
8961
0
        return false;
8962
0
      }
8963
563
    } else if (components.host_start == components.username_end &&
8964
563
               components.host_end > components.host_start) {
8965
563
      if (components.host_start == components.protocol_end + 2) {
8966
543
        if (buffer[components.protocol_end] != '/' ||
8967
543
            buffer[components.protocol_end + 1] != '/') {
8968
0
          ada_log(
8969
0
              "url_aggregator::validate missing // between protocol and host "
8970
0
              "\n",
8971
0
              to_diagram());
8972
0
          return false;
8973
0
        }
8974
543
      } else {
8975
20
        if (components.host_start > components.protocol_end &&
8976
20
            buffer[components.host_start] != '@') {
8977
0
          ada_log(
8978
0
              "url_aggregator::validate missing @ at the end of the username "
8979
0
              "\n",
8980
0
              to_diagram());
8981
0
          return false;
8982
0
        }
8983
20
      }
8984
563
    } else {
8985
0
      if (components.host_end != components.host_start) {
8986
0
        ada_log("url_aggregator::validate expected omitted host \n",
8987
0
                to_diagram());
8988
0
        return false;
8989
0
      }
8990
0
    }
8991
576
  }
8992
576
  if (components.host_end != buffer.size() &&
8993
576
      components.pathname_start > components.host_end) {
8994
24
    if (components.pathname_start == components.host_end + 2 &&
8995
11
        buffer[components.host_end] == '/' &&
8996
0
        buffer[components.host_end + 1] == '.') {
8997
0
      if (components.pathname_start + 1 >= buffer.size() ||
8998
0
          buffer[components.pathname_start] != '/' ||
8999
0
          buffer[components.pathname_start + 1] != '/') {
9000
0
        ada_log(
9001
0
            "url_aggregator::validate expected the path to begin with // \n",
9002
0
            to_diagram());
9003
0
        return false;
9004
0
      }
9005
24
    } else if (buffer[components.host_end] != ':') {
9006
0
      ada_log("url_aggregator::validate missing : at the port \n",
9007
0
              to_diagram());
9008
0
      return false;
9009
0
    }
9010
24
  }
9011
576
  if (components.pathname_start != buffer.size() &&
9012
576
      components.pathname_start < components.search_start &&
9013
576
      components.pathname_start < components.hash_start && !has_opaque_path) {
9014
576
    if (buffer[components.pathname_start] != '/') {
9015
0
      ada_log("url_aggregator::validate missing / at the path \n",
9016
0
              to_diagram());
9017
0
      return false;
9018
0
    }
9019
576
  }
9020
576
  if (components.search_start != url_components::omitted) {
9021
212
    if (buffer[components.search_start] != '?') {
9022
0
      ada_log("url_aggregator::validate missing ? at the search \n",
9023
0
              to_diagram());
9024
0
      return false;
9025
0
    }
9026
212
  }
9027
576
  if (components.hash_start != url_components::omitted) {
9028
51
    if (buffer[components.hash_start] != '#') {
9029
0
      ada_log("url_aggregator::validate missing # at the hash \n",
9030
0
              to_diagram());
9031
0
      return false;
9032
0
    }
9033
51
  }
9034
9035
576
  return true;
9036
576
}
9037
9038
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() const
9039
249
    ada_lifetime_bound {
9040
249
  ada_log("url_aggregator::get_pathname pathname_start = ",
9041
249
          components.pathname_start, " buffer.size() = ", buffer.size(),
9042
249
          " components.search_start = ", components.search_start,
9043
249
          " components.hash_start = ", components.hash_start);
9044
249
  auto ending_index = uint32_t(buffer.size());
9045
249
  if (components.search_start != url_components::omitted) {
9046
0
    ending_index = components.search_start;
9047
249
  } else if (components.hash_start != url_components::omitted) {
9048
0
    ending_index = components.hash_start;
9049
0
  }
9050
249
  return helpers::substring(buffer, components.pathname_start, ending_index);
9051
249
}
9052
9053
inline std::ostream& operator<<(std::ostream& out,
9054
0
                                const ada::url_aggregator& u) {
9055
0
  return out << u.to_string();
9056
0
}
9057
9058
0
void url_aggregator::update_host_to_base_host(const std::string_view input) {
9059
0
  ada_log("url_aggregator::update_host_to_base_host ", input);
9060
0
  ADA_ASSERT_TRUE(validate());
9061
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
9062
0
  if (type != ada::scheme::type::FILE) {
9063
    // Let host be the result of host parsing host_view with url is not special.
9064
0
    if (input.empty() && !is_special()) {
9065
0
      if (has_hostname()) {
9066
0
        clear_hostname();
9067
0
      } else if (has_dash_dot()) {
9068
0
        add_authority_slashes_if_needed();
9069
0
        delete_dash_dot();
9070
0
      }
9071
0
      return;
9072
0
    }
9073
0
  }
9074
0
  update_base_hostname(input);
9075
0
  ADA_ASSERT_TRUE(validate());
9076
0
  return;
9077
0
}
9078
}  // namespace ada
9079
9080
#endif  // ADA_URL_AGGREGATOR_INL_H
9081
/* end file include/ada/url_aggregator-inl.h */
9082
/* begin file include/ada/url_search_params.h */
9083
/**
9084
 * @file url_search_params.h
9085
 * @brief URL query string parameter manipulation.
9086
 *
9087
 * This file provides the `url_search_params` class for parsing, manipulating,
9088
 * and serializing URL query strings. It implements the URLSearchParams API
9089
 * from the WHATWG URL Standard.
9090
 *
9091
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9092
 */
9093
#ifndef ADA_URL_SEARCH_PARAMS_H
9094
#define ADA_URL_SEARCH_PARAMS_H
9095
9096
#include <optional>
9097
#include <string>
9098
#include <string_view>
9099
#include <vector>
9100
9101
namespace ada {
9102
9103
/**
9104
 * @brief Iterator types for url_search_params iteration.
9105
 */
9106
enum class url_search_params_iter_type {
9107
  KEYS,    /**< Iterate over parameter keys only */
9108
  VALUES,  /**< Iterate over parameter values only */
9109
  ENTRIES, /**< Iterate over key-value pairs */
9110
};
9111
9112
template <typename T, url_search_params_iter_type Type>
9113
struct url_search_params_iter;
9114
9115
/** Type alias for a key-value pair of string views. */
9116
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
9117
9118
/** Iterator over search parameter keys. */
9119
using url_search_params_keys_iter =
9120
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
9121
/** Iterator over search parameter values. */
9122
using url_search_params_values_iter =
9123
    url_search_params_iter<std::string_view,
9124
                           url_search_params_iter_type::VALUES>;
9125
/** Iterator over search parameter key-value pairs. */
9126
using url_search_params_entries_iter =
9127
    url_search_params_iter<key_value_view_pair,
9128
                           url_search_params_iter_type::ENTRIES>;
9129
9130
/**
9131
 * @brief Class for parsing and manipulating URL query strings.
9132
 *
9133
 * The `url_search_params` class provides methods to parse, modify, and
9134
 * serialize URL query parameters (the part after '?' in a URL). It handles
9135
 * percent-encoding and decoding automatically.
9136
 *
9137
 * All string inputs must be valid UTF-8. The caller is responsible for
9138
 * ensuring UTF-8 validity.
9139
 *
9140
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9141
 */
9142
struct url_search_params {
9143
  url_search_params() = default;
9144
9145
  /**
9146
   * Constructs url_search_params by parsing a query string.
9147
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9148
   */
9149
0
  explicit url_search_params(const std::string_view input) {
9150
0
    initialize(input);
9151
0
  }
9152
9153
  url_search_params(const url_search_params& u) = default;
9154
0
  url_search_params(url_search_params&& u) noexcept = default;
9155
  url_search_params& operator=(url_search_params&& u) noexcept = default;
9156
  url_search_params& operator=(const url_search_params& u) = default;
9157
0
  ~url_search_params() = default;
9158
9159
  /**
9160
   * Returns the number of key-value pairs.
9161
   * @return The total count of parameters.
9162
   */
9163
  [[nodiscard]] inline size_t size() const noexcept;
9164
9165
  /**
9166
   * Appends a new key-value pair to the parameter list.
9167
   * @param key The parameter name (must be valid UTF-8).
9168
   * @param value The parameter value (must be valid UTF-8).
9169
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9170
   */
9171
  inline void append(std::string_view key, std::string_view value);
9172
9173
  /**
9174
   * Removes all pairs with the given key.
9175
   * @param key The parameter name to remove.
9176
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9177
   */
9178
  inline void remove(std::string_view key);
9179
9180
  /**
9181
   * Removes all pairs with the given key and value.
9182
   * @param key The parameter name.
9183
   * @param value The parameter value to match.
9184
   */
9185
  inline void remove(std::string_view key, std::string_view value);
9186
9187
  /**
9188
   * Returns the value of the first pair with the given key.
9189
   * @param key The parameter name to search for.
9190
   * @return The value if found, or std::nullopt if not present.
9191
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9192
   */
9193
  inline std::optional<std::string_view> get(std::string_view key);
9194
9195
  /**
9196
   * Returns all values for pairs with the given key.
9197
   * @param key The parameter name to search for.
9198
   * @return A vector of all matching values (may be empty).
9199
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9200
   */
9201
  inline std::vector<std::string> get_all(std::string_view key);
9202
9203
  /**
9204
   * Checks if any pair has the given key.
9205
   * @param key The parameter name to search for.
9206
   * @return `true` if at least one pair has this key.
9207
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9208
   */
9209
  inline bool has(std::string_view key) noexcept;
9210
9211
  /**
9212
   * Checks if any pair matches the given key and value.
9213
   * @param key The parameter name to search for.
9214
   * @param value The parameter value to match.
9215
   * @return `true` if a matching pair exists.
9216
   */
9217
  inline bool has(std::string_view key, std::string_view value) noexcept;
9218
9219
  /**
9220
   * Sets a parameter value, replacing any existing pairs with the same key.
9221
   * @param key The parameter name (must be valid UTF-8).
9222
   * @param value The parameter value (must be valid UTF-8).
9223
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9224
   */
9225
  inline void set(std::string_view key, std::string_view value);
9226
9227
  /**
9228
   * Sorts all key-value pairs by their keys using code unit comparison.
9229
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9230
   */
9231
  inline void sort();
9232
9233
  /**
9234
   * Serializes the parameters to a query string (without leading '?').
9235
   * @return The percent-encoded query string.
9236
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9237
   */
9238
  inline std::string to_string() const;
9239
9240
  /**
9241
   * Returns an iterator over all parameter keys.
9242
   * Keys may repeat if there are duplicate parameters.
9243
   * @return An iterator yielding string_view keys.
9244
   * @note The iterator is invalidated if this object is modified.
9245
   */
9246
  inline url_search_params_keys_iter get_keys();
9247
9248
  /**
9249
   * Returns an iterator over all parameter values.
9250
   * @return An iterator yielding string_view values.
9251
   * @note The iterator is invalidated if this object is modified.
9252
   */
9253
  inline url_search_params_values_iter get_values();
9254
9255
  /**
9256
   * Returns an iterator over all key-value pairs.
9257
   * @return An iterator yielding key-value pair views.
9258
   * @note The iterator is invalidated if this object is modified.
9259
   */
9260
  inline url_search_params_entries_iter get_entries();
9261
9262
  /**
9263
   * C++ style conventional iterator support. const only because we
9264
   * do not really want the params to be modified via the iterator.
9265
   */
9266
0
  inline auto begin() const { return params.begin(); }
9267
0
  inline auto end() const { return params.end(); }
9268
0
  inline auto front() const { return params.front(); }
9269
0
  inline auto back() const { return params.back(); }
9270
0
  inline auto operator[](size_t index) const { return params[index]; }
9271
9272
  /**
9273
   * @private
9274
   * Used to reset the search params to a new input.
9275
   * Used primarily for C API.
9276
   * @param input
9277
   */
9278
  void reset(std::string_view input);
9279
9280
 private:
9281
  typedef std::pair<std::string, std::string> key_value_pair;
9282
  std::vector<key_value_pair> params{};
9283
9284
  /**
9285
   * The init parameter must be valid UTF-8.
9286
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9287
   */
9288
  void initialize(std::string_view init);
9289
9290
  template <typename T, url_search_params_iter_type Type>
9291
  friend struct url_search_params_iter;
9292
};  // url_search_params
9293
9294
/**
9295
 * @brief JavaScript-style iterator for url_search_params.
9296
 *
9297
 * Provides a `next()` method that returns successive values until exhausted.
9298
 * This matches the iterator pattern used in the Web Platform.
9299
 *
9300
 * @tparam T The type of value returned by the iterator.
9301
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9302
 *
9303
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9304
 */
9305
template <typename T, url_search_params_iter_type Type>
9306
struct url_search_params_iter {
9307
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()
9308
  url_search_params_iter(const url_search_params_iter& u) = default;
9309
  url_search_params_iter(url_search_params_iter&& u) noexcept = default;
9310
  url_search_params_iter& operator=(url_search_params_iter&& u) noexcept =
9311
      default;
9312
  url_search_params_iter& operator=(const url_search_params_iter& u) = default;
9313
  ~url_search_params_iter() = default;
9314
9315
  /**
9316
   * Returns the next value in the iteration sequence.
9317
   * @return The next value, or std::nullopt if iteration is complete.
9318
   */
9319
  inline std::optional<T> next();
9320
9321
  /**
9322
   * Checks if more values are available.
9323
   * @return `true` if `next()` will return a value, `false` if exhausted.
9324
   */
9325
  inline bool has_next() const;
9326
9327
 private:
9328
  static url_search_params EMPTY;
9329
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&)
9330
9331
  url_search_params& params;
9332
  size_t pos = 0;
9333
9334
  friend struct url_search_params;
9335
};
9336
9337
}  // namespace ada
9338
#endif
9339
/* end file include/ada/url_search_params.h */
9340
/* begin file include/ada/url_search_params-inl.h */
9341
/**
9342
 * @file url_search_params-inl.h
9343
 * @brief Inline declarations for the URL Search Params
9344
 */
9345
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9346
#define ADA_URL_SEARCH_PARAMS_INL_H
9347
9348
9349
#include <algorithm>
9350
#include <optional>
9351
#include <ranges>
9352
#include <string>
9353
#include <string_view>
9354
#include <vector>
9355
9356
namespace ada {
9357
9358
// A default, empty url_search_params for use with empty iterators.
9359
template <typename T, ada::url_search_params_iter_type Type>
9360
url_search_params url_search_params_iter<T, Type>::EMPTY;
9361
9362
0
inline void url_search_params::reset(std::string_view input) {
9363
0
  params.clear();
9364
0
  initialize(input);
9365
0
}
9366
9367
0
inline void url_search_params::initialize(std::string_view input) {
9368
0
  if (!input.empty() && input.front() == '?') {
9369
0
    input.remove_prefix(1);
9370
0
  }
9371
9372
0
  auto process_key_value = [&](const std::string_view current) {
9373
0
    auto equal = current.find('=');
9374
9375
0
    if (equal == std::string_view::npos) {
9376
0
      std::string name(current);
9377
0
      std::ranges::replace(name, '+', ' ');
9378
0
      params.emplace_back(unicode::percent_decode(name, name.find('%')), "");
9379
0
    } else {
9380
0
      std::string name(current.substr(0, equal));
9381
0
      std::string value(current.substr(equal + 1));
9382
9383
0
      std::ranges::replace(name, '+', ' ');
9384
0
      std::ranges::replace(value, '+', ' ');
9385
9386
0
      params.emplace_back(unicode::percent_decode(name, name.find('%')),
9387
0
                          unicode::percent_decode(value, value.find('%')));
9388
0
    }
9389
0
  };
9390
9391
0
  while (!input.empty()) {
9392
0
    auto ampersand_index = input.find('&');
9393
9394
0
    if (ampersand_index == std::string_view::npos) {
9395
0
      if (!input.empty()) {
9396
0
        process_key_value(input);
9397
0
      }
9398
0
      break;
9399
0
    } else if (ampersand_index != 0) {
9400
0
      process_key_value(input.substr(0, ampersand_index));
9401
0
    }
9402
9403
0
    input.remove_prefix(ampersand_index + 1);
9404
0
  }
9405
0
}
9406
9407
inline void url_search_params::append(const std::string_view key,
9408
0
                                      const std::string_view value) {
9409
0
  params.emplace_back(key, value);
9410
0
}
9411
9412
0
inline size_t url_search_params::size() const noexcept { return params.size(); }
9413
9414
inline std::optional<std::string_view> url_search_params::get(
9415
0
    const std::string_view key) {
9416
0
  auto entry = std::ranges::find_if(
9417
0
      params, [&key](const auto& param) { return param.first == key; });
9418
9419
0
  if (entry == params.end()) {
9420
0
    return std::nullopt;
9421
0
  }
9422
9423
0
  return entry->second;
9424
0
}
9425
9426
inline std::vector<std::string> url_search_params::get_all(
9427
0
    const std::string_view key) {
9428
0
  std::vector<std::string> out{};
9429
9430
0
  for (auto& param : params) {
9431
0
    if (param.first == key) {
9432
0
      out.emplace_back(param.second);
9433
0
    }
9434
0
  }
9435
9436
0
  return out;
9437
0
}
9438
9439
0
inline bool url_search_params::has(const std::string_view key) noexcept {
9440
0
  auto entry = std::ranges::find_if(
9441
0
      params, [&key](const auto& param) { return param.first == key; });
9442
0
  return entry != params.end();
9443
0
}
9444
9445
inline bool url_search_params::has(std::string_view key,
9446
0
                                   std::string_view value) noexcept {
9447
0
  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
9448
0
    return param.first == key && param.second == value;
9449
0
  });
9450
0
  return entry != params.end();
9451
0
}
9452
9453
0
inline std::string url_search_params::to_string() const {
9454
0
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9455
0
  std::string out{};
9456
0
  for (size_t i = 0; i < params.size(); i++) {
9457
0
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9458
0
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9459
9460
    // Performance optimization: Move this inside percent_encode.
9461
0
    std::ranges::replace(key, ' ', '+');
9462
0
    std::ranges::replace(value, ' ', '+');
9463
9464
0
    if (i != 0) {
9465
0
      out += "&";
9466
0
    }
9467
0
    out.append(key);
9468
0
    out += "=";
9469
0
    out.append(value);
9470
0
  }
9471
0
  return out;
9472
0
}
9473
9474
inline void url_search_params::set(const std::string_view key,
9475
0
                                   const std::string_view value) {
9476
0
  const auto find = [&key](const auto& param) { return param.first == key; };
9477
9478
0
  auto it = std::ranges::find_if(params, find);
9479
9480
0
  if (it == params.end()) {
9481
0
    params.emplace_back(key, value);
9482
0
  } else {
9483
0
    it->second = value;
9484
0
    params.erase(std::remove_if(std::next(it), params.end(), find),
9485
0
                 params.end());
9486
0
  }
9487
0
}
9488
9489
0
inline void url_search_params::remove(const std::string_view key) {
9490
0
  std::erase_if(params,
9491
0
                [&key](const auto& param) { return param.first == key; });
9492
0
}
9493
9494
inline void url_search_params::remove(const std::string_view key,
9495
0
                                      const std::string_view value) {
9496
0
  std::erase_if(params, [&key, &value](const auto& param) {
9497
0
    return param.first == key && param.second == value;
9498
0
  });
9499
0
}
9500
9501
0
inline void url_search_params::sort() {
9502
  // Keys are expected to be valid UTF-8, but percent_decode can produce
9503
  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
9504
0
  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
9505
0
                                      const key_value_pair& rhs) {
9506
0
    size_t i = 0, j = 0;
9507
0
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9508
0
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9509
0
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9510
0
      uint32_t codePoint1 = 0, codePoint2 = 0;
9511
9512
0
      if (low_surrogate1 != 0) {
9513
0
        codePoint1 = low_surrogate1;
9514
0
        low_surrogate1 = 0;
9515
0
      } else {
9516
0
        uint8_t c1 = uint8_t(lhs.first[i]);
9517
0
        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
9518
0
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9519
0
          i += 2;
9520
0
        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
9521
0
          codePoint1 = ((c1 & 0x0F) << 12) |
9522
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9523
0
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9524
0
          i += 3;
9525
0
        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
9526
0
          codePoint1 = ((c1 & 0x07) << 18) |
9527
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9528
0
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9529
0
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9530
0
          i += 4;
9531
9532
0
          codePoint1 -= 0x10000;
9533
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9534
0
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9535
0
          codePoint1 = high_surrogate;
9536
0
        } else {
9537
          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
9538
0
          codePoint1 = c1;
9539
0
          i++;
9540
0
        }
9541
0
      }
9542
9543
0
      if (low_surrogate2 != 0) {
9544
0
        codePoint2 = low_surrogate2;
9545
0
        low_surrogate2 = 0;
9546
0
      } else {
9547
0
        uint8_t c2 = uint8_t(rhs.first[j]);
9548
0
        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
9549
0
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
9550
0
          j += 2;
9551
0
        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
9552
0
          codePoint2 = ((c2 & 0x0F) << 12) |
9553
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
9554
0
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
9555
0
          j += 3;
9556
0
        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
9557
0
          codePoint2 = ((c2 & 0x07) << 18) |
9558
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
9559
0
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
9560
0
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
9561
0
          j += 4;
9562
0
          codePoint2 -= 0x10000;
9563
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
9564
0
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
9565
0
          codePoint2 = high_surrogate;
9566
0
        } else {
9567
          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
9568
0
          codePoint2 = c2;
9569
0
          j++;
9570
0
        }
9571
0
      }
9572
9573
0
      if (codePoint1 != codePoint2) {
9574
0
        return (codePoint1 < codePoint2);
9575
0
      }
9576
0
    }
9577
0
    return (j < rhs.first.size() || low_surrogate2 != 0);
9578
0
  });
9579
0
}
9580
9581
0
inline url_search_params_keys_iter url_search_params::get_keys() {
9582
0
  return url_search_params_keys_iter(*this);
9583
0
}
9584
9585
/**
9586
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9587
 */
9588
0
inline url_search_params_values_iter url_search_params::get_values() {
9589
0
  return url_search_params_values_iter(*this);
9590
0
}
9591
9592
/**
9593
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9594
 */
9595
0
inline url_search_params_entries_iter url_search_params::get_entries() {
9596
0
  return url_search_params_entries_iter(*this);
9597
0
}
9598
9599
template <typename T, url_search_params_iter_type Type>
9600
0
inline bool url_search_params_iter<T, Type>::has_next() const {
9601
0
  return pos < params.params.size();
9602
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
9603
9604
template <>
9605
0
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
9606
0
  if (!has_next()) {
9607
0
    return std::nullopt;
9608
0
  }
9609
0
  return params.params[pos++].first;
9610
0
}
9611
9612
template <>
9613
0
inline std::optional<std::string_view> url_search_params_values_iter::next() {
9614
0
  if (!has_next()) {
9615
0
    return std::nullopt;
9616
0
  }
9617
0
  return params.params[pos++].second;
9618
0
}
9619
9620
template <>
9621
inline std::optional<key_value_view_pair>
9622
0
url_search_params_entries_iter::next() {
9623
0
  if (!has_next()) {
9624
0
    return std::nullopt;
9625
0
  }
9626
0
  return params.params[pos++];
9627
0
}
9628
9629
}  // namespace ada
9630
9631
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
9632
/* end file include/ada/url_search_params-inl.h */
9633
9634
/* begin file include/ada/url_pattern-inl.h */
9635
/**
9636
 * @file url_pattern-inl.h
9637
 * @brief Declaration for the URLPattern inline functions.
9638
 */
9639
#ifndef ADA_URL_PATTERN_INL_H
9640
#define ADA_URL_PATTERN_INL_H
9641
9642
9643
#include <algorithm>
9644
#include <string_view>
9645
#include <utility>
9646
9647
#if ADA_INCLUDE_URL_PATTERN
9648
namespace ada {
9649
9650
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
9651
0
  return protocol == other.protocol && username == other.username &&
9652
0
         password == other.password && hostname == other.hostname &&
9653
0
         port == other.port && search == other.search && hash == other.hash &&
9654
0
         pathname == other.pathname;
9655
0
}
9656
9657
inline bool url_pattern_component_result::operator==(
9658
0
    const url_pattern_component_result& other) const {
9659
0
  return input == other.input && groups == other.groups;
9660
0
}
9661
9662
template <url_pattern_regex::regex_concept regex_provider>
9663
url_pattern_component_result
9664
url_pattern_component<regex_provider>::create_component_match_result(
9665
    std::string&& input,
9666
    std::vector<std::optional<std::string>>&& exec_result) {
9667
  // Let result be a new URLPatternComponentResult.
9668
  // Set result["input"] to input.
9669
  // Let groups be a record<USVString, (USVString or undefined)>.
9670
  auto result =
9671
      url_pattern_component_result{.input = std::move(input), .groups = {}};
9672
9673
  // We explicitly start iterating from 0 even though the spec
9674
  // says we should start from 1. This case is handled by the
9675
  // std_regex_provider which removes the full match from index 0.
9676
  // Use min() to guard against potential mismatches between
9677
  // exec_result size and group_name_list size.
9678
  const size_t size = std::min(exec_result.size(), group_name_list.size());
9679
  result.groups.reserve(size);
9680
  for (size_t index = 0; index < size; index++) {
9681
    result.groups.emplace(group_name_list[index],
9682
                          std::move(exec_result[index]));
9683
  }
9684
  return result;
9685
}
9686
9687
template <url_pattern_regex::regex_concept regex_provider>
9688
std::string_view url_pattern<regex_provider>::get_protocol() const
9689
    ada_lifetime_bound {
9690
  // Return this's associated URL pattern's protocol component's pattern string.
9691
  return protocol_component.pattern;
9692
}
9693
template <url_pattern_regex::regex_concept regex_provider>
9694
std::string_view url_pattern<regex_provider>::get_username() const
9695
    ada_lifetime_bound {
9696
  // Return this's associated URL pattern's username component's pattern string.
9697
  return username_component.pattern;
9698
}
9699
template <url_pattern_regex::regex_concept regex_provider>
9700
std::string_view url_pattern<regex_provider>::get_password() const
9701
    ada_lifetime_bound {
9702
  // Return this's associated URL pattern's password component's pattern string.
9703
  return password_component.pattern;
9704
}
9705
template <url_pattern_regex::regex_concept regex_provider>
9706
std::string_view url_pattern<regex_provider>::get_hostname() const
9707
    ada_lifetime_bound {
9708
  // Return this's associated URL pattern's hostname component's pattern string.
9709
  return hostname_component.pattern;
9710
}
9711
template <url_pattern_regex::regex_concept regex_provider>
9712
std::string_view url_pattern<regex_provider>::get_port() const
9713
    ada_lifetime_bound {
9714
  // Return this's associated URL pattern's port component's pattern string.
9715
  return port_component.pattern;
9716
}
9717
template <url_pattern_regex::regex_concept regex_provider>
9718
std::string_view url_pattern<regex_provider>::get_pathname() const
9719
    ada_lifetime_bound {
9720
  // Return this's associated URL pattern's pathname component's pattern string.
9721
  return pathname_component.pattern;
9722
}
9723
template <url_pattern_regex::regex_concept regex_provider>
9724
std::string_view url_pattern<regex_provider>::get_search() const
9725
    ada_lifetime_bound {
9726
  // Return this's associated URL pattern's search component's pattern string.
9727
  return search_component.pattern;
9728
}
9729
template <url_pattern_regex::regex_concept regex_provider>
9730
std::string_view url_pattern<regex_provider>::get_hash() const
9731
    ada_lifetime_bound {
9732
  // Return this's associated URL pattern's hash component's pattern string.
9733
  return hash_component.pattern;
9734
}
9735
template <url_pattern_regex::regex_concept regex_provider>
9736
bool url_pattern<regex_provider>::ignore_case() const {
9737
  return ignore_case_;
9738
}
9739
template <url_pattern_regex::regex_concept regex_provider>
9740
bool url_pattern<regex_provider>::has_regexp_groups() const {
9741
  // If this's associated URL pattern's has regexp groups, then return true.
9742
  return protocol_component.has_regexp_groups ||
9743
         username_component.has_regexp_groups ||
9744
         password_component.has_regexp_groups ||
9745
         hostname_component.has_regexp_groups ||
9746
         port_component.has_regexp_groups ||
9747
         pathname_component.has_regexp_groups ||
9748
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
9749
}
9750
9751
0
inline bool url_pattern_part::is_regexp() const noexcept {
9752
0
  return type == url_pattern_part_type::REGEXP;
9753
0
}
9754
9755
inline std::string_view url_pattern_compile_component_options::get_delimiter()
9756
0
    const {
9757
0
  if (delimiter) {
9758
0
    return {&delimiter.value(), 1};
9759
0
  }
9760
0
  return {};
9761
0
}
9762
9763
inline std::string_view url_pattern_compile_component_options::get_prefix()
9764
0
    const {
9765
0
  if (prefix) {
9766
0
    return {&prefix.value(), 1};
9767
0
  }
9768
0
  return {};
9769
0
}
9770
9771
template <url_pattern_regex::regex_concept regex_provider>
9772
template <url_pattern_encoding_callback F>
9773
tl::expected<url_pattern_component<regex_provider>, errors>
9774
url_pattern_component<regex_provider>::compile(
9775
    std::string_view input, F& encoding_callback,
9776
    url_pattern_compile_component_options& options) {
9777
  ada_log("url_pattern_component::compile input: ", input);
9778
  // Let part list be the result of running parse a pattern string given input,
9779
  // options, and encoding callback.
9780
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
9781
                                                             encoding_callback);
9782
9783
  if (!part_list) {
9784
    ada_log("parse_pattern_string failed");
9785
    return tl::unexpected(part_list.error());
9786
  }
9787
9788
  // Detect pattern type early to potentially skip expensive regex compilation
9789
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
9790
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
9791
9792
  url_pattern_component_type component_type =
9793
      url_pattern_component_type::REGEXP;
9794
  std::string exact_match_value{};
9795
9796
  if (part_list->empty()) {
9797
    component_type = url_pattern_component_type::EMPTY;
9798
  } else if (part_list->size() == 1) {
9799
    const auto& part = (*part_list)[0];
9800
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
9801
        part.modifier == url_pattern_part_modifier::none &&
9802
        !options.ignore_case) {
9803
      component_type = url_pattern_component_type::EXACT_MATCH;
9804
      exact_match_value = part.value;
9805
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
9806
               part.modifier == url_pattern_part_modifier::none &&
9807
               part.prefix.empty() && part.suffix.empty()) {
9808
      component_type = url_pattern_component_type::FULL_WILDCARD;
9809
    }
9810
  }
9811
9812
  // For simple patterns, skip regex generation and compilation entirely
9813
  if (component_type != url_pattern_component_type::REGEXP) {
9814
    auto pattern_string =
9815
        url_pattern_helpers::generate_pattern_string(*part_list, options);
9816
    // For FULL_WILDCARD, we need the group name from
9817
    // generate_regular_expression
9818
    std::vector<std::string> name_list;
9819
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
9820
        !part_list->empty()) {
9821
      name_list.push_back((*part_list)[0].name);
9822
    }
9823
    return url_pattern_component<regex_provider>(
9824
        std::move(pattern_string), typename regex_provider::regex_type{},
9825
        std::move(name_list), has_regexp_groups, component_type,
9826
        std::move(exact_match_value));
9827
  }
9828
9829
  // Generate regex for complex patterns
9830
  auto [regular_expression_string, name_list] =
9831
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
9832
                                                                     options);
9833
  auto pattern_string =
9834
      url_pattern_helpers::generate_pattern_string(*part_list, options);
9835
9836
  std::optional<typename regex_provider::regex_type> regular_expression =
9837
      regex_provider::create_instance(regular_expression_string,
9838
                                      options.ignore_case);
9839
  if (!regular_expression) {
9840
    return tl::unexpected(errors::type_error);
9841
  }
9842
9843
  return url_pattern_component<regex_provider>(
9844
      std::move(pattern_string), std::move(*regular_expression),
9845
      std::move(name_list), has_regexp_groups, component_type,
9846
      std::move(exact_match_value));
9847
}
9848
9849
template <url_pattern_regex::regex_concept regex_provider>
9850
bool url_pattern_component<regex_provider>::fast_test(
9851
    std::string_view input) const noexcept {
9852
  // Fast path for simple patterns - avoid regex evaluation
9853
  // Using if-else for better branch prediction on common cases
9854
  if (type == url_pattern_component_type::FULL_WILDCARD) {
9855
    return true;
9856
  }
9857
  if (type == url_pattern_component_type::EXACT_MATCH) {
9858
    return input == exact_match_value;
9859
  }
9860
  if (type == url_pattern_component_type::EMPTY) {
9861
    return input.empty();
9862
  }
9863
  // type == REGEXP
9864
  return regex_provider::regex_match(input, regexp);
9865
}
9866
9867
template <url_pattern_regex::regex_concept regex_provider>
9868
std::optional<std::vector<std::optional<std::string>>>
9869
url_pattern_component<regex_provider>::fast_match(
9870
    std::string_view input) const {
9871
  // Handle each type directly without redundant checks
9872
  if (type == url_pattern_component_type::FULL_WILDCARD) {
9873
    // FULL_WILDCARD always matches - capture the input (even if empty)
9874
    // If there's no group name, return empty groups
9875
    if (group_name_list.empty()) {
9876
      return std::vector<std::optional<std::string>>{};
9877
    }
9878
    // Capture the matched input (including empty strings)
9879
    return std::vector<std::optional<std::string>>{std::string(input)};
9880
  }
9881
  if (type == url_pattern_component_type::EXACT_MATCH) {
9882
    if (input == exact_match_value) {
9883
      return std::vector<std::optional<std::string>>{};
9884
    }
9885
    return std::nullopt;
9886
  }
9887
  if (type == url_pattern_component_type::EMPTY) {
9888
    if (input.empty()) {
9889
      return std::vector<std::optional<std::string>>{};
9890
    }
9891
    return std::nullopt;
9892
  }
9893
  // type == REGEXP - use regex
9894
  return regex_provider::regex_search(input, regexp);
9895
}
9896
9897
template <url_pattern_regex::regex_concept regex_provider>
9898
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::exec(
9899
    const url_pattern_input& input, const std::string_view* base_url) {
9900
  // Return the result of match given this's associated URL pattern, input, and
9901
  // baseURL if given.
9902
  return match(input, base_url);
9903
}
9904
9905
template <url_pattern_regex::regex_concept regex_provider>
9906
bool url_pattern<regex_provider>::test_components(
9907
    std::string_view protocol, std::string_view username,
9908
    std::string_view password, std::string_view hostname, std::string_view port,
9909
    std::string_view pathname, std::string_view search,
9910
    std::string_view hash) const {
9911
  return protocol_component.fast_test(protocol) &&
9912
         username_component.fast_test(username) &&
9913
         password_component.fast_test(password) &&
9914
         hostname_component.fast_test(hostname) &&
9915
         port_component.fast_test(port) &&
9916
         pathname_component.fast_test(pathname) &&
9917
         search_component.fast_test(search) && hash_component.fast_test(hash);
9918
}
9919
9920
template <url_pattern_regex::regex_concept regex_provider>
9921
result<bool> url_pattern<regex_provider>::test(
9922
    const url_pattern_input& input, const std::string_view* base_url_string) {
9923
  // If input is a URLPatternInit
9924
  if (std::holds_alternative<url_pattern_init>(input)) {
9925
    if (base_url_string) {
9926
      return tl::unexpected(errors::type_error);
9927
    }
9928
9929
    std::string protocol{}, username{}, password{}, hostname{};
9930
    std::string port{}, pathname{}, search{}, hash{};
9931
9932
    auto apply_result = url_pattern_init::process(
9933
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
9934
        protocol, username, password, hostname, port, pathname, search, hash);
9935
9936
    if (!apply_result) {
9937
      return false;
9938
    }
9939
9940
    std::string_view search_view = *apply_result->search;
9941
    if (search_view.starts_with("?")) {
9942
      search_view.remove_prefix(1);
9943
    }
9944
9945
    return test_components(*apply_result->protocol, *apply_result->username,
9946
                           *apply_result->password, *apply_result->hostname,
9947
                           *apply_result->port, *apply_result->pathname,
9948
                           search_view, *apply_result->hash);
9949
  }
9950
9951
  // URL string input path
9952
  result<url_aggregator> base_url;
9953
  if (base_url_string) {
9954
    base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
9955
    if (!base_url) {
9956
      return false;
9957
    }
9958
  }
9959
9960
  auto url =
9961
      ada::parse<url_aggregator>(std::get<std::string_view>(input),
9962
                                 base_url.has_value() ? &*base_url : nullptr);
9963
  if (!url) {
9964
    return false;
9965
  }
9966
9967
  // Extract components as string_view
9968
  auto protocol_view = url->get_protocol();
9969
  if (protocol_view.ends_with(":")) {
9970
    protocol_view.remove_suffix(1);
9971
  }
9972
9973
  auto search_view = url->get_search();
9974
  if (search_view.starts_with("?")) {
9975
    search_view.remove_prefix(1);
9976
  }
9977
9978
  auto hash_view = url->get_hash();
9979
  if (hash_view.starts_with("#")) {
9980
    hash_view.remove_prefix(1);
9981
  }
9982
9983
  return test_components(protocol_view, url->get_username(),
9984
                         url->get_password(), url->get_hostname(),
9985
                         url->get_port(), url->get_pathname(), search_view,
9986
                         hash_view);
9987
}
9988
9989
template <url_pattern_regex::regex_concept regex_provider>
9990
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::match(
9991
    const url_pattern_input& input, const std::string_view* base_url_string) {
9992
  std::string protocol{};
9993
  std::string username{};
9994
  std::string password{};
9995
  std::string hostname{};
9996
  std::string port{};
9997
  std::string pathname{};
9998
  std::string search{};
9999
  std::string hash{};
10000
10001
  // Let inputs be an empty list.
10002
  // Append input to inputs.
10003
  std::vector inputs{input};
10004
10005
  // If input is a URLPatternInit then:
10006
  if (std::holds_alternative<url_pattern_init>(input)) {
10007
    ada_log(
10008
        "url_pattern::match called with url_pattern_init and base_url_string=",
10009
        base_url_string);
10010
    // If baseURLString was given, throw a TypeError.
10011
    if (base_url_string) {
10012
      ada_log("failed to match because base_url_string was given");
10013
      return tl::unexpected(errors::type_error);
10014
    }
10015
10016
    // Let applyResult be the result of process a URLPatternInit given input,
10017
    // "url", protocol, username, password, hostname, port, pathname, search,
10018
    // and hash.
10019
    auto apply_result = url_pattern_init::process(
10020
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10021
        protocol, username, password, hostname, port, pathname, search, hash);
10022
10023
    // If this throws an exception, catch it, and return null.
10024
    if (!apply_result.has_value()) {
10025
      ada_log("match returned std::nullopt because process threw");
10026
      return std::nullopt;
10027
    }
10028
10029
    // Set protocol to applyResult["protocol"].
10030
    ADA_ASSERT_TRUE(apply_result->protocol.has_value());
10031
    protocol = std::move(apply_result->protocol.value());
10032
10033
    // Set username to applyResult["username"].
10034
    ADA_ASSERT_TRUE(apply_result->username.has_value());
10035
    username = std::move(apply_result->username.value());
10036
10037
    // Set password to applyResult["password"].
10038
    ADA_ASSERT_TRUE(apply_result->password.has_value());
10039
    password = std::move(apply_result->password.value());
10040
10041
    // Set hostname to applyResult["hostname"].
10042
    ADA_ASSERT_TRUE(apply_result->hostname.has_value());
10043
    hostname = std::move(apply_result->hostname.value());
10044
10045
    // Set port to applyResult["port"].
10046
    ADA_ASSERT_TRUE(apply_result->port.has_value());
10047
    port = std::move(apply_result->port.value());
10048
10049
    // Set pathname to applyResult["pathname"].
10050
    ADA_ASSERT_TRUE(apply_result->pathname.has_value());
10051
    pathname = std::move(apply_result->pathname.value());
10052
10053
    // Set search to applyResult["search"].
10054
    ADA_ASSERT_TRUE(apply_result->search.has_value());
10055
    if (apply_result->search->starts_with("?")) {
10056
      search = apply_result->search->substr(1);
10057
    } else {
10058
      search = std::move(apply_result->search.value());
10059
    }
10060
10061
    // Set hash to applyResult["hash"].
10062
    ADA_ASSERT_TRUE(apply_result->hash.has_value());
10063
    ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#"));
10064
    hash = std::move(apply_result->hash.value());
10065
  } else {
10066
    ADA_ASSERT_TRUE(std::holds_alternative<std::string_view>(input));
10067
10068
    // Let baseURL be null.
10069
    result<url_aggregator> base_url;
10070
10071
    // If baseURLString was given, then:
10072
    if (base_url_string) {
10073
      // Let baseURL be the result of parsing baseURLString.
10074
      base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10075
10076
      // If baseURL is failure, return null.
10077
      if (!base_url) {
10078
        ada_log("match returned std::nullopt because failed to parse base_url=",
10079
                *base_url_string);
10080
        return std::nullopt;
10081
      }
10082
10083
      // Append baseURLString to inputs.
10084
      inputs.emplace_back(*base_url_string);
10085
    }
10086
10087
    url_aggregator* base_url_value =
10088
        base_url.has_value() ? &*base_url : nullptr;
10089
10090
    // Set url to the result of parsing input given baseURL.
10091
    auto url = ada::parse<url_aggregator>(std::get<std::string_view>(input),
10092
                                          base_url_value);
10093
10094
    // If url is failure, return null.
10095
    if (!url) {
10096
      ada_log("match returned std::nullopt because url failed");
10097
      return std::nullopt;
10098
    }
10099
10100
    // Set protocol to url's scheme.
10101
    // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':'
10102
    // is removed. Similar work was done on workerd:
10103
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038
10104
    protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1);
10105
    // Set username to url's username.
10106
    username = url->get_username();
10107
    // Set password to url's password.
10108
    password = url->get_password();
10109
    // Set hostname to url's host, serialized, or the empty string if the value
10110
    // is null.
10111
    hostname = url->get_hostname();
10112
    // Set port to url's port, serialized, or the empty string if the value is
10113
    // null.
10114
    port = url->get_port();
10115
    // Set pathname to the result of URL path serializing url.
10116
    pathname = url->get_pathname();
10117
    // Set search to url's query or the empty string if the value is null.
10118
    // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?'
10119
    // is removed. Similar work was done on workerd:
10120
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232
10121
    if (url->has_search()) {
10122
      auto view = url->get_search();
10123
      search = view.starts_with("?") ? url->get_search().substr(1) : view;
10124
    }
10125
    // Set hash to url's fragment or the empty string if the value is null.
10126
    // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is
10127
    // removed. Similar work was done on workerd:
10128
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242
10129
    if (url->has_hash()) {
10130
      auto view = url->get_hash();
10131
      hash = view.starts_with("#") ? url->get_hash().substr(1) : view;
10132
    }
10133
  }
10134
10135
  // Use fast_match which skips regex for simple patterns (EMPTY, EXACT_MATCH,
10136
  // FULL_WILDCARD) and only falls back to regex for complex REGEXP patterns.
10137
10138
  // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol
10139
  // component's regular expression, protocol).
10140
  auto protocol_exec_result = protocol_component.fast_match(protocol);
10141
  if (!protocol_exec_result) {
10142
    return std::nullopt;
10143
  }
10144
10145
  // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username
10146
  // component's regular expression, username).
10147
  auto username_exec_result = username_component.fast_match(username);
10148
  if (!username_exec_result) {
10149
    return std::nullopt;
10150
  }
10151
10152
  // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password
10153
  // component's regular expression, password).
10154
  auto password_exec_result = password_component.fast_match(password);
10155
  if (!password_exec_result) {
10156
    return std::nullopt;
10157
  }
10158
10159
  // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname
10160
  // component's regular expression, hostname).
10161
  auto hostname_exec_result = hostname_component.fast_match(hostname);
10162
  if (!hostname_exec_result) {
10163
    return std::nullopt;
10164
  }
10165
10166
  // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's
10167
  // regular expression, port).
10168
  auto port_exec_result = port_component.fast_match(port);
10169
  if (!port_exec_result) {
10170
    return std::nullopt;
10171
  }
10172
10173
  // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname
10174
  // component's regular expression, pathname).
10175
  auto pathname_exec_result = pathname_component.fast_match(pathname);
10176
  if (!pathname_exec_result) {
10177
    return std::nullopt;
10178
  }
10179
10180
  // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's
10181
  // regular expression, search).
10182
  auto search_exec_result = search_component.fast_match(search);
10183
  if (!search_exec_result) {
10184
    return std::nullopt;
10185
  }
10186
10187
  // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's
10188
  // regular expression, hash).
10189
  auto hash_exec_result = hash_component.fast_match(hash);
10190
  if (!hash_exec_result) {
10191
    return std::nullopt;
10192
  }
10193
10194
  // Let result be a new URLPatternResult.
10195
  auto result = url_pattern_result{};
10196
  // Set result["inputs"] to inputs.
10197
  result.inputs = std::move(inputs);
10198
  // Set result["protocol"] to the result of creating a component match result
10199
  // given urlPattern's protocol component, protocol, and protocolExecResult.
10200
  result.protocol = protocol_component.create_component_match_result(
10201
      std::move(protocol), std::move(*protocol_exec_result));
10202
10203
  // Set result["username"] to the result of creating a component match result
10204
  // given urlPattern's username component, username, and usernameExecResult.
10205
  result.username = username_component.create_component_match_result(
10206
      std::move(username), std::move(*username_exec_result));
10207
10208
  // Set result["password"] to the result of creating a component match result
10209
  // given urlPattern's password component, password, and passwordExecResult.
10210
  result.password = password_component.create_component_match_result(
10211
      std::move(password), std::move(*password_exec_result));
10212
10213
  // Set result["hostname"] to the result of creating a component match result
10214
  // given urlPattern's hostname component, hostname, and hostnameExecResult.
10215
  result.hostname = hostname_component.create_component_match_result(
10216
      std::move(hostname), std::move(*hostname_exec_result));
10217
10218
  // Set result["port"] to the result of creating a component match result given
10219
  // urlPattern's port component, port, and portExecResult.
10220
  result.port = port_component.create_component_match_result(
10221
      std::move(port), std::move(*port_exec_result));
10222
10223
  // Set result["pathname"] to the result of creating a component match result
10224
  // given urlPattern's pathname component, pathname, and pathnameExecResult.
10225
  result.pathname = pathname_component.create_component_match_result(
10226
      std::move(pathname), std::move(*pathname_exec_result));
10227
10228
  // Set result["search"] to the result of creating a component match result
10229
  // given urlPattern's search component, search, and searchExecResult.
10230
  result.search = search_component.create_component_match_result(
10231
      std::move(search), std::move(*search_exec_result));
10232
10233
  // Set result["hash"] to the result of creating a component match result given
10234
  // urlPattern's hash component, hash, and hashExecResult.
10235
  result.hash = hash_component.create_component_match_result(
10236
      std::move(hash), std::move(*hash_exec_result));
10237
10238
  return result;
10239
}
10240
10241
}  // namespace ada
10242
#endif  // ADA_INCLUDE_URL_PATTERN
10243
#endif
10244
/* end file include/ada/url_pattern-inl.h */
10245
/* begin file include/ada/url_pattern_helpers-inl.h */
10246
/**
10247
 * @file url_pattern_helpers-inl.h
10248
 * @brief Declaration for the URLPattern helpers.
10249
 */
10250
#ifndef ADA_URL_PATTERN_HELPERS_INL_H
10251
#define ADA_URL_PATTERN_HELPERS_INL_H
10252
10253
#include <optional>
10254
#include <string_view>
10255
10256
10257
#if ADA_INCLUDE_URL_PATTERN
10258
namespace ada::url_pattern_helpers {
10259
#if defined(ADA_TESTING) || defined(ADA_LOGGING)
10260
0
inline std::string to_string(token_type type) {
10261
0
  switch (type) {
10262
0
    case token_type::INVALID_CHAR:
10263
0
      return "INVALID_CHAR";
10264
0
    case token_type::OPEN:
10265
0
      return "OPEN";
10266
0
    case token_type::CLOSE:
10267
0
      return "CLOSE";
10268
0
    case token_type::REGEXP:
10269
0
      return "REGEXP";
10270
0
    case token_type::NAME:
10271
0
      return "NAME";
10272
0
    case token_type::CHAR:
10273
0
      return "CHAR";
10274
0
    case token_type::ESCAPED_CHAR:
10275
0
      return "ESCAPED_CHAR";
10276
0
    case token_type::OTHER_MODIFIER:
10277
0
      return "OTHER_MODIFIER";
10278
0
    case token_type::ASTERISK:
10279
0
      return "ASTERISK";
10280
0
    case token_type::END:
10281
0
      return "END";
10282
0
    default:
10283
0
      ada::unreachable();
10284
0
  }
10285
0
}
10286
#endif  // defined(ADA_TESTING) || defined(ADA_LOGGING)
10287
10288
template <url_pattern_regex::regex_concept regex_provider>
10289
constexpr void constructor_string_parser<regex_provider>::rewind() {
10290
  // Set parser's token index to parser's component start.
10291
  token_index = component_start;
10292
  // Set parser's token increment to 0.
10293
  token_increment = 0;
10294
}
10295
10296
template <url_pattern_regex::regex_concept regex_provider>
10297
constexpr bool constructor_string_parser<regex_provider>::is_hash_prefix() {
10298
  // Return the result of running is a non-special pattern char given parser,
10299
  // parser's token index and "#".
10300
  return is_non_special_pattern_char(token_index, '#');
10301
}
10302
10303
template <url_pattern_regex::regex_concept regex_provider>
10304
constexpr bool constructor_string_parser<regex_provider>::is_search_prefix() {
10305
  // If result of running is a non-special pattern char given parser, parser's
10306
  // token index and "?" is true, then return true.
10307
  if (is_non_special_pattern_char(token_index, '?')) {
10308
    return true;
10309
  }
10310
10311
  // If parser's token list[parser's token index]'s value is not "?", then
10312
  // return false.
10313
  if (token_list[token_index].value != "?") {
10314
    return false;
10315
  }
10316
10317
  // If previous index is less than 0, then return true.
10318
  if (token_index == 0) return true;
10319
  // Let previous index be parser's token index - 1.
10320
  auto previous_index = token_index - 1;
10321
  // Let previous token be the result of running get a safe token given parser
10322
  // and previous index.
10323
  auto previous_token = get_safe_token(previous_index);
10324
  ADA_ASSERT_TRUE(previous_token);
10325
  // If any of the following are true, then return false:
10326
  // - previous token's type is "name".
10327
  // - previous token's type is "regexp".
10328
  // - previous token's type is "close".
10329
  // - previous token's type is "asterisk".
10330
  return !(previous_token->type == token_type::NAME ||
10331
           previous_token->type == token_type::REGEXP ||
10332
           previous_token->type == token_type::CLOSE ||
10333
           previous_token->type == token_type::ASTERISK);
10334
}
10335
10336
template <url_pattern_regex::regex_concept regex_provider>
10337
constexpr bool
10338
constructor_string_parser<regex_provider>::is_non_special_pattern_char(
10339
    size_t index, uint32_t value) const {
10340
  // Let token be the result of running get a safe token given parser and index.
10341
  auto token = get_safe_token(index);
10342
  ADA_ASSERT_TRUE(token);
10343
10344
  // If token's value is not value, then return false.
10345
  // TODO: Remove this once we make sure get_safe_token returns a non-empty
10346
  // string.
10347
  if (!token->value.empty() &&
10348
      static_cast<uint32_t>(token->value[0]) != value) {
10349
    return false;
10350
  }
10351
10352
  // If any of the following are true:
10353
  // - token's type is "char";
10354
  // - token's type is "escaped-char"; or
10355
  // - token's type is "invalid-char",
10356
  // - then return true.
10357
  return token->type == token_type::CHAR ||
10358
         token->type == token_type::ESCAPED_CHAR ||
10359
         token->type == token_type::INVALID_CHAR;
10360
}
10361
10362
template <url_pattern_regex::regex_concept regex_provider>
10363
constexpr const token*
10364
constructor_string_parser<regex_provider>::get_safe_token(size_t index) const {
10365
  // If index is less than parser's token list's size, then return parser's
10366
  // token list[index].
10367
  if (index < token_list.size()) [[likely]] {
10368
    return &token_list[index];
10369
  }
10370
10371
  // Assert: parser's token list's size is greater than or equal to 1.
10372
  ADA_ASSERT_TRUE(!token_list.empty());
10373
10374
  // Let token be parser's token list[last index].
10375
  // Assert: token's type is "end".
10376
  ADA_ASSERT_TRUE(token_list.back().type == token_type::END);
10377
10378
  // Return token.
10379
  return &token_list.back();
10380
}
10381
10382
template <url_pattern_regex::regex_concept regex_provider>
10383
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10384
    const {
10385
  // If parser's token list[parser's token index]'s type is "open", then return
10386
  // true.
10387
  return token_list[token_index].type == token_type::OPEN;
10388
}
10389
10390
template <url_pattern_regex::regex_concept regex_provider>
10391
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10392
    const {
10393
  // If parser's token list[parser's token index]'s type is "close", then return
10394
  // true.
10395
  return token_list[token_index].type == token_type::CLOSE;
10396
}
10397
10398
template <url_pattern_regex::regex_concept regex_provider>
10399
constexpr bool
10400
constructor_string_parser<regex_provider>::next_is_authority_slashes() const {
10401
  // If the result of running is a non-special pattern char given parser,
10402
  // parser's token index + 1, and "/" is false, then return false.
10403
  if (!is_non_special_pattern_char(token_index + 1, '/')) {
10404
    return false;
10405
  }
10406
  // If the result of running is a non-special pattern char given parser,
10407
  // parser's token index + 2, and "/" is false, then return false.
10408
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10409
    return false;
10410
  }
10411
  return true;
10412
}
10413
10414
template <url_pattern_regex::regex_concept regex_provider>
10415
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10416
    const {
10417
  // Return the result of running is a non-special pattern char given parser,
10418
  // parser's token index, and ":".
10419
  return is_non_special_pattern_char(token_index, ':');
10420
}
10421
10422
template <url_pattern_regex::regex_concept regex_provider>
10423
void constructor_string_parser<regex_provider>::change_state(State new_state,
10424
                                                             size_t skip) {
10425
  // If parser's state is not "init", not "authority", and not "done", then set
10426
  // parser's result[parser's state] to the result of running make a component
10427
  // string given parser.
10428
  if (state != State::INIT && state != State::AUTHORITY &&
10429
      state != State::DONE) {
10430
    auto value = make_component_string();
10431
    // TODO: Simplify this.
10432
    switch (state) {
10433
      case State::PROTOCOL: {
10434
        result.protocol = value;
10435
        break;
10436
      }
10437
      case State::USERNAME: {
10438
        result.username = value;
10439
        break;
10440
      }
10441
      case State::PASSWORD: {
10442
        result.password = value;
10443
        break;
10444
      }
10445
      case State::HOSTNAME: {
10446
        result.hostname = value;
10447
        break;
10448
      }
10449
      case State::PORT: {
10450
        result.port = value;
10451
        break;
10452
      }
10453
      case State::PATHNAME: {
10454
        result.pathname = value;
10455
        break;
10456
      }
10457
      case State::SEARCH: {
10458
        result.search = value;
10459
        break;
10460
      }
10461
      case State::HASH: {
10462
        result.hash = value;
10463
        break;
10464
      }
10465
      default:
10466
        ada::unreachable();
10467
    }
10468
  }
10469
10470
  // If parser's state is not "init" and new state is not "done", then:
10471
  if (state != State::INIT && new_state != State::DONE) {
10472
    // If parser's state is "protocol", "authority", "username", or "password";
10473
    // new state is "port", "pathname", "search", or "hash"; and parser's
10474
    // result["hostname"] does not exist, then set parser's result["hostname"]
10475
    // to the empty string.
10476
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10477
         state == State::USERNAME || state == State::PASSWORD) &&
10478
        (new_state == State::PORT || new_state == State::PATHNAME ||
10479
         new_state == State::SEARCH || new_state == State::HASH) &&
10480
        !result.hostname)
10481
      result.hostname = "";
10482
  }
10483
10484
  // If parser's state is "protocol", "authority", "username", "password",
10485
  // "hostname", or "port"; new state is "search" or "hash"; and parser's
10486
  // result["pathname"] does not exist, then:
10487
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10488
       state == State::USERNAME || state == State::PASSWORD ||
10489
       state == State::HOSTNAME || state == State::PORT) &&
10490
      (new_state == State::SEARCH || new_state == State::HASH) &&
10491
      !result.pathname) {
10492
    if (protocol_matches_a_special_scheme_flag) {
10493
      result.pathname = "/";
10494
    } else {
10495
      // Otherwise, set parser's result["pathname"] to the empty string.
10496
      result.pathname = "";
10497
    }
10498
  }
10499
10500
  // If parser's state is "protocol", "authority", "username", "password",
10501
  // "hostname", "port", or "pathname"; new state is "hash"; and parser's
10502
  // result["search"] does not exist, then set parser's result["search"] to
10503
  // the empty string.
10504
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10505
       state == State::USERNAME || state == State::PASSWORD ||
10506
       state == State::HOSTNAME || state == State::PORT ||
10507
       state == State::PATHNAME) &&
10508
      new_state == State::HASH && !result.search) {
10509
    result.search = "";
10510
  }
10511
10512
  // Set parser's state to new state.
10513
  state = new_state;
10514
  // Increment parser's token index by skip.
10515
  token_index += skip;
10516
  // Set parser's component start to parser's token index.
10517
  component_start = token_index;
10518
  // Set parser's token increment to 0.
10519
  token_increment = 0;
10520
}
10521
10522
template <url_pattern_regex::regex_concept regex_provider>
10523
std::string constructor_string_parser<regex_provider>::make_component_string() {
10524
  // Assert: parser's token index is less than parser's token list's size.
10525
  ADA_ASSERT_TRUE(token_index < token_list.size());
10526
10527
  // Let token be parser's token list[parser's token index].
10528
  // Let end index be token's index.
10529
  const auto end_index = token_list[token_index].index;
10530
  // Let component start token be the result of running get a safe token given
10531
  // parser and parser's component start.
10532
  const auto component_start_token = get_safe_token(component_start);
10533
  ADA_ASSERT_TRUE(component_start_token);
10534
  // Let component start input index be component start token's index.
10535
  const auto component_start_input_index = component_start_token->index;
10536
  // Return the code point substring from component start input index to end
10537
  // index within parser's input.
10538
  return std::string(input.substr(component_start_input_index,
10539
                                  end_index - component_start_input_index));
10540
}
10541
10542
template <url_pattern_regex::regex_concept regex_provider>
10543
constexpr bool
10544
constructor_string_parser<regex_provider>::is_an_identity_terminator() const {
10545
  // Return the result of running is a non-special pattern char given parser,
10546
  // parser's token index, and "@".
10547
  return is_non_special_pattern_char(token_index, '@');
10548
}
10549
10550
template <url_pattern_regex::regex_concept regex_provider>
10551
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
10552
    const {
10553
  // Return the result of running is a non-special pattern char given parser,
10554
  // parser's token index, and "/".
10555
  return is_non_special_pattern_char(token_index, '/');
10556
}
10557
10558
template <url_pattern_regex::regex_concept regex_provider>
10559
constexpr bool constructor_string_parser<regex_provider>::is_password_prefix()
10560
    const {
10561
  // Return the result of running is a non-special pattern char given parser,
10562
  // parser's token index, and ":".
10563
  return is_non_special_pattern_char(token_index, ':');
10564
}
10565
10566
template <url_pattern_regex::regex_concept regex_provider>
10567
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_open()
10568
    const {
10569
  // Return the result of running is a non-special pattern char given parser,
10570
  // parser's token index, and "[".
10571
  return is_non_special_pattern_char(token_index, '[');
10572
}
10573
10574
template <url_pattern_regex::regex_concept regex_provider>
10575
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
10576
    const {
10577
  // Return the result of running is a non-special pattern char given parser,
10578
  // parser's token index, and "]".
10579
  return is_non_special_pattern_char(token_index, ']');
10580
}
10581
10582
template <url_pattern_regex::regex_concept regex_provider>
10583
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
10584
    const {
10585
  // Return the result of running is a non-special pattern char given parser,
10586
  // parser's token index, and ":".
10587
  return is_non_special_pattern_char(token_index, ':');
10588
}
10589
10590
0
constexpr void Tokenizer::get_next_code_point() {
10591
0
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
10592
0
  ADA_ASSERT_TRUE(next_index < input.size());
10593
  // this assumes that we have a valid, non-truncated UTF-8 stream.
10594
0
  code_point = 0;
10595
0
  size_t number_bytes = 0;
10596
0
  unsigned char first_byte = input[next_index];
10597
10598
0
  if ((first_byte & 0x80) == 0) {
10599
    // 1-byte character (ASCII)
10600
0
    next_index++;
10601
0
    code_point = first_byte;
10602
0
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
10603
0
            uint32_t(code_point));
10604
0
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10605
0
            " input.size()=", input.size());
10606
0
    return;
10607
0
  }
10608
0
  ada_log("Tokenizer::get_next_code_point read first byte=",
10609
0
          uint32_t(first_byte));
10610
0
  if ((first_byte & 0xE0) == 0xC0) {
10611
0
    code_point = first_byte & 0x1F;
10612
0
    number_bytes = 2;
10613
0
    ada_log("Tokenizer::get_next_code_point two bytes");
10614
0
  } else if ((first_byte & 0xF0) == 0xE0) {
10615
0
    code_point = first_byte & 0x0F;
10616
0
    number_bytes = 3;
10617
0
    ada_log("Tokenizer::get_next_code_point three bytes");
10618
0
  } else if ((first_byte & 0xF8) == 0xF0) {
10619
0
    code_point = first_byte & 0x07;
10620
0
    number_bytes = 4;
10621
0
    ada_log("Tokenizer::get_next_code_point four bytes");
10622
0
  }
10623
0
  ADA_ASSERT_TRUE(number_bytes + next_index <= input.size());
10624
10625
0
  for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) {
10626
0
    unsigned char byte = input[i];
10627
0
    ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte));
10628
0
    code_point = (code_point << 6) | (byte & 0x3F);
10629
0
  }
10630
0
  ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=",
10631
0
          uint32_t(code_point));
10632
0
  ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10633
0
          " input.size()=", input.size());
10634
0
  next_index += number_bytes;
10635
0
}
10636
10637
0
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
10638
0
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
10639
0
          new_index);
10640
  // Set tokenizer's next index to index.
10641
0
  next_index = new_index;
10642
  // Run get the next code point given tokenizer.
10643
0
  get_next_code_point();
10644
0
}
10645
10646
inline void Tokenizer::add_token(token_type type, size_t next_position,
10647
0
                                 size_t value_position, size_t value_length) {
10648
0
  ada_log("Tokenizer::add_token called with type=", to_string(type),
10649
0
          " next_position=", next_position, " value_position=", value_position);
10650
0
  ADA_ASSERT_TRUE(next_position >= value_position);
10651
10652
  // Let token be a new token.
10653
  // Set token's type to type.
10654
  // Set token's index to tokenizer's index.
10655
  // Set token's value to the code point substring from value position with
10656
  // length value length within tokenizer's input.
10657
  // Append token to the back of tokenizer's token list.
10658
0
  token_list.emplace_back(type, index,
10659
0
                          input.substr(value_position, value_length));
10660
  // Set tokenizer's index to next position.
10661
0
  index = next_position;
10662
0
}
10663
10664
inline void Tokenizer::add_token_with_default_length(token_type type,
10665
                                                     size_t next_position,
10666
0
                                                     size_t value_position) {
10667
  // Let computed length be next position - value position.
10668
0
  auto computed_length = next_position - value_position;
10669
  // Run add a token given tokenizer, type, next position, value position, and
10670
  // computed length.
10671
0
  add_token(type, next_position, value_position, computed_length);
10672
0
}
10673
10674
0
inline void Tokenizer::add_token_with_defaults(token_type type) {
10675
0
  ada_log("Tokenizer::add_token_with_defaults called with type=",
10676
0
          to_string(type));
10677
  // Run add a token with default length given tokenizer, type, tokenizer's next
10678
  // index, and tokenizer's index.
10679
0
  add_token_with_default_length(type, next_index, index);
10680
0
}
10681
10682
inline ada_warn_unused std::optional<errors>
10683
Tokenizer::process_tokenizing_error(size_t next_position,
10684
0
                                    size_t value_position) {
10685
  // If tokenizer's policy is "strict", then throw a TypeError.
10686
0
  if (policy == token_policy::strict) {
10687
0
    ada_log("process_tokenizing_error failed with next_position=",
10688
0
            next_position, " value_position=", value_position);
10689
0
    return errors::type_error;
10690
0
  }
10691
  // Assert: tokenizer's policy is "lenient".
10692
0
  ADA_ASSERT_TRUE(policy == token_policy::lenient);
10693
  // Run add a token with default length given tokenizer, "invalid-char", next
10694
  // position, and value position.
10695
0
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
10696
0
                                value_position);
10697
0
  return std::nullopt;
10698
0
}
10699
10700
template <url_pattern_encoding_callback F>
10701
token* url_pattern_parser<F>::try_consume_modifier_token() {
10702
  // Let token be the result of running try to consume a token given parser and
10703
  // "other-modifier".
10704
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
10705
  // If token is not null, then return token.
10706
  if (token) return token;
10707
  // Set token to the result of running try to consume a token given parser and
10708
  // "asterisk".
10709
  // Return token.
10710
  return try_consume_token(token_type::ASTERISK);
10711
}
10712
10713
template <url_pattern_encoding_callback F>
10714
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
10715
    const token* name_token) {
10716
  // Let token be the result of running try to consume a token given parser and
10717
  // "regexp".
10718
  auto token = try_consume_token(token_type::REGEXP);
10719
  // If name token is null and token is null, then set token to the result of
10720
  // running try to consume a token given parser and "asterisk".
10721
  if (!name_token && !token) {
10722
    token = try_consume_token(token_type::ASTERISK);
10723
  }
10724
  // Return token.
10725
  return token;
10726
}
10727
10728
template <url_pattern_encoding_callback F>
10729
token* url_pattern_parser<F>::try_consume_token(token_type type) {
10730
  ada_log("url_pattern_parser::try_consume_token called with type=",
10731
          to_string(type));
10732
  // Assert: parser's index is less than parser's token list size.
10733
  ADA_ASSERT_TRUE(index < tokens.size());
10734
  // Let next token be parser's token list[parser's index].
10735
  auto& next_token = tokens[index];
10736
  // If next token's type is not type return null.
10737
  if (next_token.type != type) return nullptr;
10738
  // Increase parser's index by 1.
10739
  index++;
10740
  // Return next token.
10741
  return &next_token;
10742
}
10743
10744
template <url_pattern_encoding_callback F>
10745
std::string url_pattern_parser<F>::consume_text() {
10746
  // Let result be the empty string.
10747
  std::string result{};
10748
  // While true:
10749
  while (true) {
10750
    // Let token be the result of running try to consume a token given parser
10751
    // and "char".
10752
    auto token = try_consume_token(token_type::CHAR);
10753
    // If token is null, then set token to the result of running try to consume
10754
    // a token given parser and "escaped-char".
10755
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
10756
    // If token is null, then break.
10757
    if (!token) break;
10758
    // Append token's value to the end of result.
10759
    result.append(token->value);
10760
  }
10761
  // Return result.
10762
  return result;
10763
}
10764
10765
template <url_pattern_encoding_callback F>
10766
bool url_pattern_parser<F>::consume_required_token(token_type type) {
10767
  ada_log("url_pattern_parser::consume_required_token called with type=",
10768
          to_string(type));
10769
  // Let result be the result of running try to consume a token given parser and
10770
  // type.
10771
  return try_consume_token(type) != nullptr;
10772
}
10773
10774
template <url_pattern_encoding_callback F>
10775
std::optional<errors>
10776
url_pattern_parser<F>::maybe_add_part_from_the_pending_fixed_value() {
10777
  // If parser's pending fixed value is the empty string, then return.
10778
  if (pending_fixed_value.empty()) {
10779
    ada_log("pending_fixed_value is empty");
10780
    return std::nullopt;
10781
  }
10782
  // Let encoded value be the result of running parser's encoding callback given
10783
  // parser's pending fixed value.
10784
  auto encoded_value = encoding_callback(pending_fixed_value);
10785
  if (!encoded_value) {
10786
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
10787
    return encoded_value.error();
10788
  }
10789
  // Set parser's pending fixed value to the empty string.
10790
  pending_fixed_value.clear();
10791
  // Let part be a new part whose type is "fixed-text", value is encoded value,
10792
  // and modifier is "none".
10793
  // Append part to parser's part list.
10794
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
10795
                     std::move(*encoded_value),
10796
                     url_pattern_part_modifier::none);
10797
  return std::nullopt;
10798
}
10799
10800
template <url_pattern_encoding_callback F>
10801
std::optional<errors> url_pattern_parser<F>::add_part(
10802
    std::string_view prefix, token* name_token, token* regexp_or_wildcard_token,
10803
    std::string_view suffix, token* modifier_token) {
10804
  // Let modifier be "none".
10805
  auto modifier = url_pattern_part_modifier::none;
10806
  // If modifier token is not null:
10807
  if (modifier_token) {
10808
    // If modifier token's value is "?" then set modifier to "optional".
10809
    if (modifier_token->value == "?") {
10810
      modifier = url_pattern_part_modifier::optional;
10811
    } else if (modifier_token->value == "*") {
10812
      // Otherwise if modifier token's value is "*" then set modifier to
10813
      // "zero-or-more".
10814
      modifier = url_pattern_part_modifier::zero_or_more;
10815
    } else if (modifier_token->value == "+") {
10816
      // Otherwise if modifier token's value is "+" then set modifier to
10817
      // "one-or-more".
10818
      modifier = url_pattern_part_modifier::one_or_more;
10819
    }
10820
  }
10821
  // If name token is null and regexp or wildcard token is null and modifier
10822
  // is "none":
10823
  if (!name_token && !regexp_or_wildcard_token &&
10824
      modifier == url_pattern_part_modifier::none) {
10825
    // Append prefix to the end of parser's pending fixed value.
10826
    pending_fixed_value.append(prefix);
10827
    return std::nullopt;
10828
  }
10829
  // Run maybe add a part from the pending fixed value given parser.
10830
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
10831
    return *error;
10832
  }
10833
  // If name token is null and regexp or wildcard token is null:
10834
  if (!name_token && !regexp_or_wildcard_token) {
10835
    // Assert: suffix is the empty string.
10836
    ADA_ASSERT_TRUE(suffix.empty());
10837
    // If prefix is the empty string, then return.
10838
    if (prefix.empty()) return std::nullopt;
10839
    // Let encoded value be the result of running parser's encoding callback
10840
    // given prefix.
10841
    auto encoded_value = encoding_callback(prefix);
10842
    if (!encoded_value) {
10843
      return encoded_value.error();
10844
    }
10845
    // Let part be a new part whose type is "fixed-text", value is encoded
10846
    // value, and modifier is modifier.
10847
    // Append part to parser's part list.
10848
    parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
10849
                       std::move(*encoded_value), modifier);
10850
    return std::nullopt;
10851
  }
10852
  // Let regexp value be the empty string.
10853
  std::string regexp_value{};
10854
  // If regexp or wildcard token is null, then set regexp value to parser's
10855
  // segment wildcard regexp.
10856
  if (!regexp_or_wildcard_token) {
10857
    regexp_value = segment_wildcard_regexp;
10858
  } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) {
10859
    // Otherwise if regexp or wildcard token's type is "asterisk", then set
10860
    // regexp value to the full wildcard regexp value.
10861
    regexp_value = ".*";
10862
  } else {
10863
    // Otherwise set regexp value to regexp or wildcard token's value.
10864
    regexp_value = regexp_or_wildcard_token->value;
10865
  }
10866
  // Let type be "regexp".
10867
  auto type = url_pattern_part_type::REGEXP;
10868
  // If regexp value is parser's segment wildcard regexp:
10869
  if (regexp_value == segment_wildcard_regexp) {
10870
    // Set type to "segment-wildcard".
10871
    type = url_pattern_part_type::SEGMENT_WILDCARD;
10872
    // Set regexp value to the empty string.
10873
    regexp_value.clear();
10874
  } else if (regexp_value == ".*") {
10875
    // Otherwise if regexp value is the full wildcard regexp value:
10876
    // Set type to "full-wildcard".
10877
    type = url_pattern_part_type::FULL_WILDCARD;
10878
    // Set regexp value to the empty string.
10879
    regexp_value.clear();
10880
  }
10881
  // Let name be the empty string.
10882
  std::string name{};
10883
  // If name token is not null, then set name to name token's value.
10884
  if (name_token) {
10885
    name = name_token->value;
10886
  } else if (regexp_or_wildcard_token != nullptr) {
10887
    // Otherwise if regexp or wildcard token is not null:
10888
    // Set name to parser's next numeric name, serialized.
10889
    name = std::to_string(next_numeric_name);
10890
    // Increment parser's next numeric name by 1.
10891
    next_numeric_name++;
10892
  }
10893
  // If the result of running is a duplicate name given parser and name is
10894
  // true, then throw a TypeError.
10895
  if (std::ranges::any_of(
10896
          parts, [&name](const auto& part) { return part.name == name; })) {
10897
    return errors::type_error;
10898
  }
10899
  // Let encoded prefix be the result of running parser's encoding callback
10900
  // given prefix.
10901
  auto encoded_prefix = encoding_callback(prefix);
10902
  if (!encoded_prefix) return encoded_prefix.error();
10903
  // Let encoded suffix be the result of running parser's encoding callback
10904
  // given suffix.
10905
  auto encoded_suffix = encoding_callback(suffix);
10906
  if (!encoded_suffix) return encoded_suffix.error();
10907
  // Let part be a new part whose type is type, value is regexp value,
10908
  // modifier is modifier, name is name, prefix is encoded prefix, and suffix
10909
  // is encoded suffix.
10910
  // Append part to parser's part list.
10911
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
10912
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
10913
  return std::nullopt;
10914
}
10915
10916
template <url_pattern_encoding_callback F>
10917
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
10918
    std::string_view input, url_pattern_compile_component_options& options,
10919
    F& encoding_callback) {
10920
  ada_log("parse_pattern_string input=", input);
10921
  // Let parser be a new pattern parser whose encoding callback is encoding
10922
  // callback and segment wildcard regexp is the result of running generate a
10923
  // segment wildcard regexp given options.
10924
  auto parser = url_pattern_parser<F>(
10925
      encoding_callback, generate_segment_wildcard_regexp(options));
10926
  // Set parser's token list to the result of running tokenize given input and
10927
  // "strict".
10928
  auto tokenize_result = tokenize(input, token_policy::strict);
10929
  if (!tokenize_result) {
10930
    ada_log("parse_pattern_string tokenize failed");
10931
    return tl::unexpected(tokenize_result.error());
10932
  }
10933
  parser.tokens = std::move(*tokenize_result);
10934
10935
  // While parser's index is less than parser's token list's size:
10936
  while (parser.can_continue()) {
10937
    // Let char token be the result of running try to consume a token given
10938
    // parser and "char".
10939
    auto char_token = parser.try_consume_token(token_type::CHAR);
10940
    // Let name token be the result of running try to consume a token given
10941
    // parser and "name".
10942
    auto name_token = parser.try_consume_token(token_type::NAME);
10943
    // Let regexp or wildcard token be the result of running try to consume a
10944
    // regexp or wildcard token given parser and name token.
10945
    auto regexp_or_wildcard_token =
10946
        parser.try_consume_regexp_or_wildcard_token(name_token);
10947
    // If name token is not null or regexp or wildcard token is not null:
10948
    if (name_token || regexp_or_wildcard_token) {
10949
      // Let prefix be the empty string.
10950
      std::string prefix{};
10951
      // If char token is not null then set prefix to char token's value.
10952
      if (char_token) prefix = char_token->value;
10953
      // If prefix is not the empty string and not options's prefix code point:
10954
      if (!prefix.empty() && prefix != options.get_prefix()) {
10955
        // Append prefix to the end of parser's pending fixed value.
10956
        parser.pending_fixed_value.append(prefix);
10957
        // Set prefix to the empty string.
10958
        prefix.clear();
10959
      }
10960
      // Run maybe add a part from the pending fixed value given parser.
10961
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
10962
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
10963
        return tl::unexpected(*error);
10964
      }
10965
      // Let modifier token be the result of running try to consume a modifier
10966
      // token given parser.
10967
      auto modifier_token = parser.try_consume_modifier_token();
10968
      // Run add a part given parser, prefix, name token, regexp or wildcard
10969
      // token, the empty string, and modifier token.
10970
      if (auto error =
10971
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
10972
                              modifier_token)) {
10973
        ada_log("parser.add_part failed");
10974
        return tl::unexpected(*error);
10975
      }
10976
      // Continue.
10977
      continue;
10978
    }
10979
10980
    // Let fixed token be char token.
10981
    auto fixed_token = char_token;
10982
    // If fixed token is null, then set fixed token to the result of running try
10983
    // to consume a token given parser and "escaped-char".
10984
    if (!fixed_token)
10985
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
10986
    // If fixed token is not null:
10987
    if (fixed_token) {
10988
      // Append fixed token's value to parser's pending fixed value.
10989
      parser.pending_fixed_value.append(fixed_token->value);
10990
      // Continue.
10991
      continue;
10992
    }
10993
    // Let open token be the result of running try to consume a token given
10994
    // parser and "open".
10995
    auto open_token = parser.try_consume_token(token_type::OPEN);
10996
    // If open token is not null:
10997
    if (open_token) {
10998
      // Set prefix be the result of running consume text given parser.
10999
      auto prefix_ = parser.consume_text();
11000
      // Set name token to the result of running try to consume a token given
11001
      // parser and "name".
11002
      name_token = parser.try_consume_token(token_type::NAME);
11003
      // Set regexp or wildcard token to the result of running try to consume a
11004
      // regexp or wildcard token given parser and name token.
11005
      regexp_or_wildcard_token =
11006
          parser.try_consume_regexp_or_wildcard_token(name_token);
11007
      // Let suffix be the result of running consume text given parser.
11008
      auto suffix_ = parser.consume_text();
11009
      // Run consume a required token given parser and "close".
11010
      if (!parser.consume_required_token(token_type::CLOSE)) {
11011
        ada_log("parser.consume_required_token failed");
11012
        return tl::unexpected(errors::type_error);
11013
      }
11014
      // Set modifier token to the result of running try to consume a modifier
11015
      // token given parser.
11016
      auto modifier_token = parser.try_consume_modifier_token();
11017
      // Run add a part given parser, prefix, name token, regexp or wildcard
11018
      // token, suffix, and modifier token.
11019
      if (auto error =
11020
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
11021
                              suffix_, modifier_token)) {
11022
        return tl::unexpected(*error);
11023
      }
11024
      // Continue.
11025
      continue;
11026
    }
11027
    // Run maybe add a part from the pending fixed value given parser.
11028
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11029
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
11030
      return tl::unexpected(*error);
11031
    }
11032
    // Run consume a required token given parser and "end".
11033
    if (!parser.consume_required_token(token_type::END)) {
11034
      return tl::unexpected(errors::type_error);
11035
    }
11036
  }
11037
  ada_log("parser.parts size is: ", parser.parts.size());
11038
  // Return parser's part list.
11039
  return parser.parts;
11040
}
11041
11042
template <url_pattern_regex::regex_concept regex_provider>
11043
bool protocol_component_matches_special_scheme(
11044
    url_pattern_component<regex_provider>& component) {
11045
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
11046
  switch (component.type) {
11047
    case url_pattern_component_type::EMPTY:
11048
      // Empty pattern can't match any special scheme
11049
      return false;
11050
    case url_pattern_component_type::EXACT_MATCH:
11051
      // Direct string comparison for exact match patterns
11052
      return component.exact_match_value == "http" ||
11053
             component.exact_match_value == "https" ||
11054
             component.exact_match_value == "ws" ||
11055
             component.exact_match_value == "wss" ||
11056
             component.exact_match_value == "ftp";
11057
    case url_pattern_component_type::FULL_WILDCARD:
11058
      // Full wildcard matches everything including special schemes
11059
      return true;
11060
    case url_pattern_component_type::REGEXP:
11061
      // Fall back to regex matching for complex patterns
11062
      auto& regex = component.regexp;
11063
      return regex_provider::regex_match("http", regex) ||
11064
             regex_provider::regex_match("https", regex) ||
11065
             regex_provider::regex_match("ws", regex) ||
11066
             regex_provider::regex_match("wss", regex) ||
11067
             regex_provider::regex_match("ftp", regex);
11068
  }
11069
  ada::unreachable();
11070
}
11071
11072
template <url_pattern_regex::regex_concept regex_provider>
11073
inline std::optional<errors> constructor_string_parser<
11074
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
11075
  ada_log(
11076
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
11077
      "flag");
11078
  // Let protocol string be the result of running make a component string given
11079
  // parser.
11080
  auto protocol_string = make_component_string();
11081
  // Let protocol component be the result of compiling a component given
11082
  // protocol string, canonicalize a protocol, and default options.
11083
  auto protocol_component = url_pattern_component<regex_provider>::compile(
11084
      protocol_string, canonicalize_protocol,
11085
      url_pattern_compile_component_options::DEFAULT);
11086
  if (!protocol_component) {
11087
    ada_log("url_pattern_component::compile failed for protocol_string ",
11088
            protocol_string);
11089
    return protocol_component.error();
11090
  }
11091
  // If the result of running protocol component matches a special scheme given
11092
  // protocol component is true, then set parser's protocol matches a special
11093
  // scheme flag to true.
11094
  if (protocol_component_matches_special_scheme(*protocol_component)) {
11095
    protocol_matches_a_special_scheme_flag = true;
11096
  }
11097
  return std::nullopt;
11098
}
11099
11100
template <url_pattern_regex::regex_concept regex_provider>
11101
tl::expected<url_pattern_init, errors>
11102
constructor_string_parser<regex_provider>::parse(std::string_view input) {
11103
  ada_log("constructor_string_parser::parse input=", input);
11104
  // Let parser be a new constructor string parser whose input is input and
11105
  // token list is the result of running tokenize given input and "lenient".
11106
  auto token_list = tokenize(input, token_policy::lenient);
11107
  if (!token_list) {
11108
    return tl::unexpected(token_list.error());
11109
  }
11110
  auto parser = constructor_string_parser(input, std::move(*token_list));
11111
11112
  // While parser's token index is less than parser's token list size:
11113
  while (parser.token_index < parser.token_list.size()) {
11114
    // Set parser's token increment to 1.
11115
    parser.token_increment = 1;
11116
11117
    // If parser's token list[parser's token index]'s type is "end" then:
11118
    if (parser.token_list[parser.token_index].type == token_type::END) {
11119
      // If parser's state is "init":
11120
      if (parser.state == State::INIT) {
11121
        // Run rewind given parser.
11122
        parser.rewind();
11123
        // If the result of running is a hash prefix given parser is true, then
11124
        // run change state given parser, "hash" and 1.
11125
        if (parser.is_hash_prefix()) {
11126
          parser.change_state(State::HASH, 1);
11127
        } else if (parser.is_search_prefix()) {
11128
          // Otherwise if the result of running is a search prefix given parser
11129
          // is true: Run change state given parser, "search" and 1.
11130
          parser.change_state(State::SEARCH, 1);
11131
        } else {
11132
          // Run change state given parser, "pathname" and 0.
11133
          parser.change_state(State::PATHNAME, 0);
11134
        }
11135
        // Increment parser's token index by parser's token increment.
11136
        parser.token_index += parser.token_increment;
11137
        // Continue.
11138
        continue;
11139
      }
11140
11141
      if (parser.state == State::AUTHORITY) {
11142
        // If parser's state is "authority":
11143
        // Run rewind and set state given parser, and "hostname".
11144
        parser.rewind();
11145
        parser.change_state(State::HOSTNAME, 0);
11146
        // Increment parser's token index by parser's token increment.
11147
        parser.token_index += parser.token_increment;
11148
        // Continue.
11149
        continue;
11150
      }
11151
11152
      // Run change state given parser, "done" and 0.
11153
      parser.change_state(State::DONE, 0);
11154
      // Break.
11155
      break;
11156
    }
11157
11158
    // If the result of running is a group open given parser is true:
11159
    if (parser.is_group_open()) {
11160
      // Increment parser's group depth by 1.
11161
      parser.group_depth += 1;
11162
      // Increment parser's token index by parser's token increment.
11163
      parser.token_index += parser.token_increment;
11164
    }
11165
11166
    // If parser's group depth is greater than 0:
11167
    if (parser.group_depth > 0) {
11168
      // If the result of running is a group close given parser is true, then
11169
      // decrement parser's group depth by 1.
11170
      if (parser.is_group_close()) {
11171
        parser.group_depth -= 1;
11172
      } else {
11173
        // Increment parser's token index by parser's token increment.
11174
        parser.token_index += parser.token_increment;
11175
        continue;
11176
      }
11177
    }
11178
11179
    // Switch on parser's state and run the associated steps:
11180
    switch (parser.state) {
11181
      case State::INIT: {
11182
        // If the result of running is a protocol suffix given parser is true:
11183
        if (parser.is_protocol_suffix()) {
11184
          // Run rewind and set state given parser and "protocol".
11185
          parser.rewind();
11186
          parser.change_state(State::PROTOCOL, 0);
11187
        }
11188
        break;
11189
      }
11190
      case State::PROTOCOL: {
11191
        // If the result of running is a protocol suffix given parser is true:
11192
        if (parser.is_protocol_suffix()) {
11193
          // Run compute protocol matches a special scheme flag given parser.
11194
          if (const auto error =
11195
                  parser.compute_protocol_matches_special_scheme_flag()) {
11196
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11197
            return tl::unexpected(*error);
11198
          }
11199
          // Let next state be "pathname".
11200
          auto next_state = State::PATHNAME;
11201
          // Let skip be 1.
11202
          auto skip = 1;
11203
          // If the result of running next is authority slashes given parser is
11204
          // true:
11205
          if (parser.next_is_authority_slashes()) {
11206
            // Set next state to "authority".
11207
            next_state = State::AUTHORITY;
11208
            // Set skip to 3.
11209
            skip = 3;
11210
          } else if (parser.protocol_matches_a_special_scheme_flag) {
11211
            // Otherwise if parser's protocol matches a special scheme flag is
11212
            // true, then set next state to "authority".
11213
            next_state = State::AUTHORITY;
11214
          }
11215
11216
          // Run change state given parser, next state, and skip.
11217
          parser.change_state(next_state, skip);
11218
        }
11219
        break;
11220
      }
11221
      case State::AUTHORITY: {
11222
        // If the result of running is an identity terminator given parser is
11223
        // true, then run rewind and set state given parser and "username".
11224
        if (parser.is_an_identity_terminator()) {
11225
          parser.rewind();
11226
          parser.change_state(State::USERNAME, 0);
11227
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11228
                   parser.is_hash_prefix()) {
11229
          // Otherwise if any of the following are true:
11230
          // - the result of running is a pathname start given parser;
11231
          // - the result of running is a search prefix given parser; or
11232
          // - the result of running is a hash prefix given parser,
11233
          // then run rewind and set state given parser and "hostname".
11234
          parser.rewind();
11235
          parser.change_state(State::HOSTNAME, 0);
11236
        }
11237
        break;
11238
      }
11239
      case State::USERNAME: {
11240
        // If the result of running is a password prefix given parser is true,
11241
        // then run change state given parser, "password", and 1.
11242
        if (parser.is_password_prefix()) {
11243
          parser.change_state(State::PASSWORD, 1);
11244
        } else if (parser.is_an_identity_terminator()) {
11245
          // Otherwise if the result of running is an identity terminator given
11246
          // parser is true, then run change state given parser, "hostname",
11247
          // and 1.
11248
          parser.change_state(State::HOSTNAME, 1);
11249
        }
11250
        break;
11251
      }
11252
      case State::PASSWORD: {
11253
        // If the result of running is an identity terminator given parser is
11254
        // true, then run change state given parser, "hostname", and 1.
11255
        if (parser.is_an_identity_terminator()) {
11256
          parser.change_state(State::HOSTNAME, 1);
11257
        }
11258
        break;
11259
      }
11260
      case State::HOSTNAME: {
11261
        // If the result of running is an IPv6 open given parser is true, then
11262
        // increment parser's hostname IPv6 bracket depth by 1.
11263
        if (parser.is_an_ipv6_open()) {
11264
          parser.hostname_ipv6_bracket_depth += 1;
11265
        } else if (parser.is_an_ipv6_close()) {
11266
          // Otherwise if the result of running is an IPv6 close given parser is
11267
          // true, then decrement parser's hostname IPv6 bracket depth by 1.
11268
          parser.hostname_ipv6_bracket_depth -= 1;
11269
        } else if (parser.is_port_prefix() &&
11270
                   parser.hostname_ipv6_bracket_depth == 0) {
11271
          // Otherwise if the result of running is a port prefix given parser is
11272
          // true and parser's hostname IPv6 bracket depth is zero, then run
11273
          // change state given parser, "port", and 1.
11274
          parser.change_state(State::PORT, 1);
11275
        } else if (parser.is_pathname_start()) {
11276
          // Otherwise if the result of running is a pathname start given parser
11277
          // is true, then run change state given parser, "pathname", and 0.
11278
          parser.change_state(State::PATHNAME, 0);
11279
        } else if (parser.is_search_prefix()) {
11280
          // Otherwise if the result of running is a search prefix given parser
11281
          // is true, then run change state given parser, "search", and 1.
11282
          parser.change_state(State::SEARCH, 1);
11283
        } else if (parser.is_hash_prefix()) {
11284
          // Otherwise if the result of running is a hash prefix given parser is
11285
          // true, then run change state given parser, "hash", and 1.
11286
          parser.change_state(State::HASH, 1);
11287
        }
11288
11289
        break;
11290
      }
11291
      case State::PORT: {
11292
        // If the result of running is a pathname start given parser is true,
11293
        // then run change state given parser, "pathname", and 0.
11294
        if (parser.is_pathname_start()) {
11295
          parser.change_state(State::PATHNAME, 0);
11296
        } else if (parser.is_search_prefix()) {
11297
          // Otherwise if the result of running is a search prefix given parser
11298
          // is true, then run change state given parser, "search", and 1.
11299
          parser.change_state(State::SEARCH, 1);
11300
        } else if (parser.is_hash_prefix()) {
11301
          // Otherwise if the result of running is a hash prefix given parser is
11302
          // true, then run change state given parser, "hash", and 1.
11303
          parser.change_state(State::HASH, 1);
11304
        }
11305
        break;
11306
      }
11307
      case State::PATHNAME: {
11308
        // If the result of running is a search prefix given parser is true,
11309
        // then run change state given parser, "search", and 1.
11310
        if (parser.is_search_prefix()) {
11311
          parser.change_state(State::SEARCH, 1);
11312
        } else if (parser.is_hash_prefix()) {
11313
          // Otherwise if the result of running is a hash prefix given parser is
11314
          // true, then run change state given parser, "hash", and 1.
11315
          parser.change_state(State::HASH, 1);
11316
        }
11317
        break;
11318
      }
11319
      case State::SEARCH: {
11320
        // If the result of running is a hash prefix given parser is true, then
11321
        // run change state given parser, "hash", and 1.
11322
        if (parser.is_hash_prefix()) {
11323
          parser.change_state(State::HASH, 1);
11324
        }
11325
        break;
11326
      }
11327
      case State::HASH: {
11328
        // Do nothing
11329
        break;
11330
      }
11331
      default: {
11332
        // Assert: This step is never reached.
11333
        unreachable();
11334
      }
11335
    }
11336
11337
    // Increment parser's token index by parser's token increment.
11338
    parser.token_index += parser.token_increment;
11339
  }
11340
11341
  // If parser's result contains "hostname" and not "port", then set parser's
11342
  // result["port"] to the empty string.
11343
  if (parser.result.hostname && !parser.result.port) {
11344
    parser.result.port = "";
11345
  }
11346
11347
  // Return parser's result.
11348
  return parser.result;
11349
}
11350
11351
}  // namespace ada::url_pattern_helpers
11352
#endif  // ADA_INCLUDE_URL_PATTERN
11353
#endif
11354
/* end file include/ada/url_pattern_helpers-inl.h */
11355
11356
// Public API
11357
/* begin file include/ada/ada_version.h */
11358
/**
11359
 * @file ada_version.h
11360
 * @brief Definitions for Ada's version number.
11361
 */
11362
#ifndef ADA_ADA_VERSION_H
11363
#define ADA_ADA_VERSION_H
11364
11365
0
#define ADA_VERSION "3.4.4"
11366
11367
namespace ada {
11368
11369
enum {
11370
  ADA_VERSION_MAJOR = 3,
11371
  ADA_VERSION_MINOR = 4,
11372
  ADA_VERSION_REVISION = 4,
11373
};
11374
11375
}  // namespace ada
11376
11377
#endif  // ADA_ADA_VERSION_H
11378
/* end file include/ada/ada_version.h */
11379
/* begin file include/ada/implementation-inl.h */
11380
/**
11381
 * @file implementation-inl.h
11382
 */
11383
#ifndef ADA_IMPLEMENTATION_INL_H
11384
#define ADA_IMPLEMENTATION_INL_H
11385
11386
11387
11388
#include <variant>
11389
#include <string_view>
11390
11391
namespace ada {
11392
11393
#if ADA_INCLUDE_URL_PATTERN
11394
template <url_pattern_regex::regex_concept regex_provider>
11395
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
11396
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
11397
                  const std::string_view* base_url,
11398
                  const url_pattern_options* options) {
11399
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
11400
                                                        base_url, options);
11401
}
11402
#endif  // ADA_INCLUDE_URL_PATTERN
11403
11404
}  // namespace ada
11405
11406
#endif  // ADA_IMPLEMENTATION_INL_H
11407
/* end file include/ada/implementation-inl.h */
11408
11409
#endif  // ADA_H
11410
/* end file include/ada.h */