Coverage Report

Created: 2026-06-10 06:28

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-06-03 08:26:49 -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-05-25 17:39:11 -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
712k
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1032
712k
  return !!(a[i >> 3] & (1 << (i & 7)));
1033
712k
}
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
0
constexpr bool has_hex_prefix_unsafe(std::string_view input) {
1187
  // This is actually efficient code, see has_hex_prefix for the assembly.
1188
0
  constexpr bool is_little_endian = std::endian::native == std::endian::little;
1189
0
  constexpr uint16_t word0x = 0x7830;
1190
0
  uint16_t two_first_bytes =
1191
0
      static_cast<uint16_t>(input[0]) |
1192
0
      static_cast<uint16_t>((static_cast<uint16_t>(input[1]) << 8));
1193
0
  if constexpr (is_little_endian) {
1194
0
    two_first_bytes |= 0x2000;
1195
  } else {
1196
    two_first_bytes |= 0x020;
1197
  }
1198
0
  return two_first_bytes == word0x;
1199
0
}
1200
1201
0
constexpr bool has_hex_prefix(std::string_view input) {
1202
0
  return input.size() >= 2 && has_hex_prefix_unsafe(input);
1203
0
}
1204
1205
0
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1206
1207
5.21k
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1208
1209
2.60k
constexpr bool is_alpha(char x) noexcept {
1210
2.60k
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1211
2.60k
}
1212
1213
0
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1214
0
  return input.size() >= 2 &&
1215
0
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1216
0
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1217
0
                                  input[2] == '?' || input[2] == '#'));
1218
0
}
1219
1220
constexpr bool is_normalized_windows_drive_letter(
1221
0
    std::string_view input) noexcept {
1222
0
  return input.size() >= 2 && (is_alpha(input[0]) && (input[1] == ':'));
1223
0
}
1224
1225
ada_really_inline constexpr uint64_t try_parse_ipv4_fast(
1226
2.60k
    std::string_view input) noexcept {
1227
2.60k
  const char* p = input.data();
1228
2.60k
  const char* const pend = p + input.size();
1229
1230
2.60k
  uint32_t ipv4 = 0;
1231
1232
2.60k
  for (int i = 0; i < 4; ++i) {
1233
2.60k
    if (p == pend) {
1234
0
      return ipv4_fast_fail;
1235
0
    }
1236
1237
2.60k
    uint32_t val;
1238
2.60k
    char c = *p;
1239
2.60k
    if (c >= '0' && c <= '9') {
1240
0
      val = c - '0';
1241
0
      p++;
1242
2.60k
    } else {
1243
2.60k
      return ipv4_fast_fail;
1244
2.60k
    }
1245
1246
0
    if (p < pend) {
1247
0
      c = *p;
1248
0
      if (c >= '0' && c <= '9') {
1249
0
        if (val == 0) return ipv4_fast_fail;
1250
0
        val = val * 10 + (c - '0');
1251
0
        p++;
1252
0
        if (p < pend) {
1253
0
          c = *p;
1254
0
          if (c >= '0' && c <= '9') {
1255
0
            val = val * 10 + (c - '0');
1256
0
            p++;
1257
0
            if (val > 255) return ipv4_fast_fail;
1258
0
          }
1259
0
        }
1260
0
      }
1261
0
    }
1262
1263
0
    ipv4 = (ipv4 << 8) | val;
1264
1265
0
    if (i < 3) {
1266
0
      if (p == pend || *p != '.') {
1267
0
        return ipv4_fast_fail;
1268
0
      }
1269
0
      p++;
1270
0
    }
1271
0
  }
1272
1273
0
  if (p != pend) {
1274
0
    if (p == pend - 1 && *p == '.') {
1275
0
      return ipv4;
1276
0
    }
1277
0
    return ipv4_fast_fail;
1278
0
  }
1279
1280
0
  return ipv4;
1281
0
}
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
6.58k
  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
4.02k
                                                       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
4.02k
  return input.substr(pos1, pos2 - pos1);
1721
4.02k
}
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
0
inline void inner_concat(std::string& buffer, T t) {
1772
0
  buffer.append(t);
1773
0
}
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: void ada::helpers::inner_concat<char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
1774
1775
/**
1776
 * @private
1777
 */
1778
template <typename T, typename... Args>
1779
0
inline void inner_concat(std::string& buffer, T t, Args... args) {
1780
0
  buffer.append(t);
1781
0
  return inner_concat(buffer, args...);
1782
0
}
Unexecuted instantiation: void ada::helpers::inner_concat<char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Unexecuted instantiation: void ada::helpers::inner_concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Unexecuted instantiation: void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
1783
1784
/**
1785
 * @private
1786
 * Concatenate the arguments and return a string.
1787
 * @returns a string
1788
 */
1789
template <typename... Args>
1790
0
std::string concat(Args... args) {
1791
0
  std::string answer;
1792
0
  inner_concat(answer, args...);
1793
0
  return answer;
1794
0
}
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
1795
1796
/**
1797
 * @private
1798
 * @return Number of leading zeroes.
1799
 */
1800
2.60k
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
2.60k
  return __builtin_clz(input_num);
1807
2.60k
#endif  // ADA_REGULAR_VISUAL_STUDIO
1808
2.60k
}
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
0
inline int fast_digit_count(uint32_t x) noexcept {
1817
0
  auto int_log2 = [](uint32_t z) -> int {
1818
0
    return 31 - ada::helpers::leading_zeroes(z | 1);
1819
0
  };
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
0
  const static uint64_t table[] = {
1825
0
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
1826
0
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
1827
0
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
1828
0
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
1829
0
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
1830
0
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
1831
0
      42949672960, 42949672960};
1832
0
  return int((x + table[int_log2(x)]) >> 32);
1833
0
}
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
7.82k
#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
0
  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
0
  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
2.60k
      : 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
2.60k
      : 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
0
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
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
2.60k
  ~expected_storage_base() {
2494
2.60k
    if (m_has_val) {
2495
2.60k
      m_val.~T();
2496
2.60k
    }
2497
2.60k
  }
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
2.60k
  ~expected_storage_base() {
2494
2.60k
    if (m_has_val) {
2495
2.60k
      m_val.~T();
2496
2.60k
    }
2497
2.60k
  }
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
2.60k
  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
2.60k
  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
7.82k
  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
7.82k
  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
2.60k
      : impl_base(in_place, std::forward<Args>(args)...),
3457
2.60k
        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
2.60k
      : impl_base(in_place, std::forward<Args>(args)...),
3457
2.60k
        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
0
      : impl_base(unexpect, std::move(e.value())),
3498
0
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Unexecuted instantiation: _ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
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
2.60k
      : 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
2.60k
      : 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
7.82k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
3866
7.82k
    TL_ASSERT(has_value());
3867
7.82k
    return valptr();
3868
7.82k
  }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator->()
tl::expected<ada::url_aggregator, ada::errors>::operator->()
Line
Count
Source
3865
7.82k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
3866
7.82k
    TL_ASSERT(has_value());
3867
7.82k
    return valptr();
3868
7.82k
  }
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
7.82k
  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
7.82k
  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
2.60k
  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
2.60k
  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
2.60k
  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
  // True when the most recent decoded unit was malformed UTF-8.
5979
0
  bool had_invalid_code_point() const { return invalid_code_point; }
5980
5981
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
5982
  constexpr void seek_and_get_next_code_point(size_t index);
5983
5984
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
5985
5986
  void add_token(token_type type, size_t next_position, size_t value_position,
5987
                 size_t value_length);
5988
5989
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
5990
  void add_token_with_default_length(token_type type, size_t next_position,
5991
                                     size_t value_position);
5992
5993
  // @see
5994
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
5995
  void add_token_with_defaults(token_type type);
5996
5997
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
5998
  std::optional<errors> process_tokenizing_error(
5999
      size_t next_position, size_t value_position) ada_warn_unused;
6000
6001
  friend tl::expected<std::vector<token>, errors> tokenize(
6002
      std::string_view input, token_policy policy);
6003
6004
 private:
6005
  // has an associated input, a pattern string, initially the empty string.
6006
  std::string_view input;
6007
  // has an associated policy, a tokenize policy, initially "strict".
6008
  token_policy policy;
6009
  // has an associated token list, a token list, initially an empty list.
6010
  std::vector<token> token_list{};
6011
  // has an associated index, a number, initially 0.
6012
  size_t index = 0;
6013
  // has an associated next index, a number, initially 0.
6014
  size_t next_index = 0;
6015
  // has an associated code point, a Unicode code point, initially null.
6016
  char32_t code_point{};
6017
  // Tracks whether the last decoded code point was malformed UTF-8.
6018
  bool invalid_code_point = false;
6019
};
6020
6021
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
6022
template <url_pattern_regex::regex_concept regex_provider>
6023
struct constructor_string_parser {
6024
  explicit constructor_string_parser(std::string_view new_input,
6025
                                     std::vector<token>&& new_token_list)
6026
      : input(new_input), token_list(std::move(new_token_list)) {}
6027
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
6028
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
6029
6030
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
6031
  enum class State {
6032
    INIT,
6033
    PROTOCOL,
6034
    AUTHORITY,
6035
    USERNAME,
6036
    PASSWORD,
6037
    HOSTNAME,
6038
    PORT,
6039
    PATHNAME,
6040
    SEARCH,
6041
    HASH,
6042
    DONE,
6043
  };
6044
6045
  // @see
6046
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6047
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6048
6049
 private:
6050
  // @see https://urlpattern.spec.whatwg.org/#rewind
6051
  constexpr void rewind();
6052
6053
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6054
  constexpr bool is_hash_prefix();
6055
6056
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6057
  constexpr bool is_search_prefix();
6058
6059
  // @see https://urlpattern.spec.whatwg.org/#change-state
6060
  void change_state(State state, size_t skip);
6061
6062
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6063
  constexpr bool is_group_open() const;
6064
6065
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6066
  constexpr bool is_group_close() const;
6067
6068
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6069
  constexpr bool is_protocol_suffix() const;
6070
6071
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6072
  constexpr bool next_is_authority_slashes() const;
6073
6074
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6075
  constexpr bool is_an_identity_terminator() const;
6076
6077
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6078
  constexpr bool is_pathname_start() const;
6079
6080
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6081
  constexpr bool is_password_prefix() const;
6082
6083
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6084
  constexpr bool is_an_ipv6_open() const;
6085
6086
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6087
  constexpr bool is_an_ipv6_close() const;
6088
6089
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6090
  constexpr bool is_port_prefix() const;
6091
6092
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6093
  constexpr bool is_non_special_pattern_char(size_t index,
6094
                                             uint32_t value) const;
6095
6096
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6097
  constexpr const token* get_safe_token(size_t index) const;
6098
6099
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6100
  std::string make_component_string();
6101
  // has an associated input, a string, which must be set upon creation.
6102
  std::string_view input;
6103
  // has an associated token list, a token list, which must be set upon
6104
  // creation.
6105
  std::vector<token> token_list;
6106
  // has an associated result, a URLPatternInit, initially set to a new
6107
  // URLPatternInit.
6108
  url_pattern_init result{};
6109
  // has an associated component start, a number, initially set to 0.
6110
  size_t component_start = 0;
6111
  // has an associated token index, a number, initially set to 0.
6112
  size_t token_index = 0;
6113
  // has an associated token increment, a number, initially set to 1.
6114
  size_t token_increment = 1;
6115
  // has an associated group depth, a number, initially set to 0.
6116
  size_t group_depth = 0;
6117
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6118
  // 0.
6119
  size_t hostname_ipv6_bracket_depth = 0;
6120
  // has an associated protocol matches a special scheme flag, a boolean,
6121
  // initially set to false.
6122
  bool protocol_matches_a_special_scheme_flag = false;
6123
  // has an associated state, a string, initially set to "init".
6124
  State state = State::INIT;
6125
};
6126
6127
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6128
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6129
6130
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6131
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6132
6133
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6134
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6135
6136
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6137
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6138
6139
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6140
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6141
    std::string_view input);
6142
6143
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6144
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6145
6146
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6147
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6148
    std::string_view input, std::string_view protocol);
6149
6150
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6151
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6152
6153
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6154
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6155
    std::string_view input);
6156
6157
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6158
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6159
6160
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6161
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6162
6163
// @see https://urlpattern.spec.whatwg.org/#tokenize
6164
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6165
                                                  token_policy policy);
6166
6167
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6168
std::string process_base_url_string(std::string_view input,
6169
                                    url_pattern_init::process_type type);
6170
6171
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6172
std::string escape_pattern_string(std::string_view input);
6173
6174
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6175
std::string escape_regexp_string(std::string_view input);
6176
6177
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6178
constexpr bool is_absolute_pathname(
6179
    std::string_view input, url_pattern_init::process_type type) noexcept;
6180
6181
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6182
template <url_pattern_encoding_callback F>
6183
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6184
    std::string_view input, url_pattern_compile_component_options& options,
6185
    F& encoding_callback);
6186
6187
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6188
std::string generate_pattern_string(
6189
    std::vector<url_pattern_part>& part_list,
6190
    url_pattern_compile_component_options& options);
6191
6192
// @see
6193
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6194
std::tuple<std::string, std::vector<std::string>>
6195
generate_regular_expression_and_name_list(
6196
    const std::vector<url_pattern_part>& part_list,
6197
    url_pattern_compile_component_options options);
6198
6199
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6200
bool is_ipv6_address(std::string_view input) noexcept;
6201
6202
// @see
6203
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6204
template <url_pattern_regex::regex_concept regex_provider>
6205
bool protocol_component_matches_special_scheme(
6206
    ada::url_pattern_component<regex_provider>& input);
6207
6208
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6209
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6210
6211
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6212
std::string generate_segment_wildcard_regexp(
6213
    url_pattern_compile_component_options options);
6214
6215
}  // namespace ada::url_pattern_helpers
6216
#endif  // ADA_INCLUDE_URL_PATTERN
6217
#endif
6218
/* end file include/ada/url_pattern_helpers.h */
6219
6220
#include <string>
6221
#include <string_view>
6222
#include <variant>
6223
6224
namespace ada::parser {
6225
#if ADA_INCLUDE_URL_PATTERN
6226
template <url_pattern_regex::regex_concept regex_provider>
6227
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6228
    std::variant<std::string_view, url_pattern_init>&& input,
6229
    const std::string_view* base_url, const url_pattern_options* options) {
6230
  // Let init be null.
6231
  url_pattern_init init;
6232
6233
  // If input is a scalar value string then:
6234
  if (std::holds_alternative<std::string_view>(input)) {
6235
    // Set init to the result of running parse a constructor string given input.
6236
    auto parse_result =
6237
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6238
            std::get<std::string_view>(input));
6239
    if (!parse_result) {
6240
      ada_log("constructor_string_parser::parse failed");
6241
      return tl::unexpected(parse_result.error());
6242
    }
6243
    init = std::move(*parse_result);
6244
    // If baseURL is null and init["protocol"] does not exist, then throw a
6245
    // TypeError.
6246
    if (!base_url && !init.protocol) {
6247
      ada_log("base url is null and protocol is not set");
6248
      return tl::unexpected(errors::type_error);
6249
    }
6250
6251
    // If baseURL is not null, set init["baseURL"] to baseURL.
6252
    if (base_url) {
6253
      init.base_url = std::string(*base_url);
6254
    }
6255
  } else {
6256
    // Assert: input is a URLPatternInit.
6257
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6258
    // If baseURL is not null, then throw a TypeError.
6259
    if (base_url) {
6260
      ada_log("base url is not null");
6261
      return tl::unexpected(errors::type_error);
6262
    }
6263
    // Optimization: Avoid copy by moving the input value.
6264
    // Set init to input.
6265
    init = std::move(std::get<url_pattern_init>(input));
6266
  }
6267
6268
  // Let processedInit be the result of process a URLPatternInit given init,
6269
  // "pattern", null, null, null, null, null, null, null, and null.
6270
  auto processed_init =
6271
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6272
  if (!processed_init) {
6273
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6274
    return tl::unexpected(processed_init.error());
6275
  }
6276
6277
  // For each componentName of  "protocol", "username", "password", "hostname",
6278
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6279
  // not exist, then set processedInit[componentName] to "*".
6280
  ADA_ASSERT_TRUE(processed_init.has_value());
6281
  if (!processed_init->protocol) processed_init->protocol = "*";
6282
  if (!processed_init->username) processed_init->username = "*";
6283
  if (!processed_init->password) processed_init->password = "*";
6284
  if (!processed_init->hostname) processed_init->hostname = "*";
6285
  if (!processed_init->port) processed_init->port = "*";
6286
  if (!processed_init->pathname) processed_init->pathname = "*";
6287
  if (!processed_init->search) processed_init->search = "*";
6288
  if (!processed_init->hash) processed_init->hash = "*";
6289
6290
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6291
  ada_log("-- processed_init->username: ", processed_init->username.value());
6292
  ada_log("-- processed_init->password: ", processed_init->password.value());
6293
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6294
  ada_log("-- processed_init->port: ", processed_init->port.value());
6295
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6296
  ada_log("-- processed_init->search: ", processed_init->search.value());
6297
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6298
6299
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6300
  // is a string which represents its corresponding default port in radix-10
6301
  // using ASCII digits then set processedInit["port"] to the empty string.
6302
  // TODO: Optimization opportunity.
6303
  if (scheme::is_special(*processed_init->protocol)) {
6304
    std::string_view port = processed_init->port.value();
6305
    if (std::to_string(scheme::get_special_port(*processed_init->protocol)) ==
6306
        port) {
6307
      processed_init->port->clear();
6308
    }
6309
  }
6310
6311
  // Let urlPattern be a new URL pattern.
6312
  url_pattern<regex_provider> url_pattern_{};
6313
6314
  // Set urlPattern's protocol component to the result of compiling a component
6315
  // given processedInit["protocol"], canonicalize a protocol, and default
6316
  // options.
6317
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6318
      processed_init->protocol.value(),
6319
      url_pattern_helpers::canonicalize_protocol,
6320
      url_pattern_compile_component_options::DEFAULT);
6321
  if (!protocol_component) {
6322
    ada_log("url_pattern_component::compile failed for protocol ",
6323
            processed_init->protocol.value());
6324
    return tl::unexpected(protocol_component.error());
6325
  }
6326
  url_pattern_.protocol_component = std::move(*protocol_component);
6327
6328
  // Set urlPattern's username component to the result of compiling a component
6329
  // given processedInit["username"], canonicalize a username, and default
6330
  // options.
6331
  auto username_component = url_pattern_component<regex_provider>::compile(
6332
      processed_init->username.value(),
6333
      url_pattern_helpers::canonicalize_username,
6334
      url_pattern_compile_component_options::DEFAULT);
6335
  if (!username_component) {
6336
    ada_log("url_pattern_component::compile failed for username ",
6337
            processed_init->username.value());
6338
    return tl::unexpected(username_component.error());
6339
  }
6340
  url_pattern_.username_component = std::move(*username_component);
6341
6342
  // Set urlPattern's password component to the result of compiling a component
6343
  // given processedInit["password"], canonicalize a password, and default
6344
  // options.
6345
  auto password_component = url_pattern_component<regex_provider>::compile(
6346
      processed_init->password.value(),
6347
      url_pattern_helpers::canonicalize_password,
6348
      url_pattern_compile_component_options::DEFAULT);
6349
  if (!password_component) {
6350
    ada_log("url_pattern_component::compile failed for password ",
6351
            processed_init->password.value());
6352
    return tl::unexpected(password_component.error());
6353
  }
6354
  url_pattern_.password_component = std::move(*password_component);
6355
6356
  // TODO: Optimization opportunity. The following if statement can be
6357
  // simplified.
6358
  // If the result running hostname pattern is an IPv6 address given
6359
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6360
  // to the result of compiling a component given processedInit["hostname"],
6361
  // canonicalize an IPv6 hostname, and hostname options.
6362
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6363
    ada_log("processed_init->hostname is ipv6 address");
6364
    // then set urlPattern's hostname component to the result of compiling a
6365
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6366
    // and hostname options.
6367
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6368
        processed_init->hostname.value(),
6369
        url_pattern_helpers::canonicalize_ipv6_hostname,
6370
        url_pattern_compile_component_options::DEFAULT);
6371
    if (!hostname_component) {
6372
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6373
              processed_init->hostname.value());
6374
      return tl::unexpected(hostname_component.error());
6375
    }
6376
    url_pattern_.hostname_component = std::move(*hostname_component);
6377
  } else {
6378
    // Otherwise, set urlPattern's hostname component to the result of compiling
6379
    // a component given processedInit["hostname"], canonicalize a hostname, and
6380
    // hostname options.
6381
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6382
        processed_init->hostname.value(),
6383
        url_pattern_helpers::canonicalize_hostname,
6384
        url_pattern_compile_component_options::HOSTNAME);
6385
    if (!hostname_component) {
6386
      ada_log("url_pattern_component::compile failed for hostname ",
6387
              processed_init->hostname.value());
6388
      return tl::unexpected(hostname_component.error());
6389
    }
6390
    url_pattern_.hostname_component = std::move(*hostname_component);
6391
  }
6392
6393
  // Set urlPattern's port component to the result of compiling a component
6394
  // given processedInit["port"], canonicalize a port, and default options.
6395
  auto port_component = url_pattern_component<regex_provider>::compile(
6396
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6397
      url_pattern_compile_component_options::DEFAULT);
6398
  if (!port_component) {
6399
    ada_log("url_pattern_component::compile failed for port ",
6400
            processed_init->port.value());
6401
    return tl::unexpected(port_component.error());
6402
  }
6403
  url_pattern_.port_component = std::move(*port_component);
6404
6405
  // Let compileOptions be a copy of the default options with the ignore case
6406
  // property set to options["ignoreCase"].
6407
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6408
  if (options) {
6409
    compile_options.ignore_case = options->ignore_case;
6410
  }
6411
6412
  // TODO: Optimization opportunity: Simplify this if statement.
6413
  // If the result of running protocol component matches a special scheme given
6414
  // urlPattern's protocol component is true, then:
6415
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6416
          regex_provider>(url_pattern_.protocol_component)) {
6417
    // Let pathCompileOptions be copy of the pathname options with the ignore
6418
    // case property set to options["ignoreCase"].
6419
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6420
    if (options) {
6421
      path_compile_options.ignore_case = options->ignore_case;
6422
    }
6423
6424
    // Set urlPattern's pathname component to the result of compiling a
6425
    // component given processedInit["pathname"], canonicalize a pathname, and
6426
    // pathCompileOptions.
6427
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6428
        processed_init->pathname.value(),
6429
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6430
    if (!pathname_component) {
6431
      ada_log("url_pattern_component::compile failed for pathname ",
6432
              processed_init->pathname.value());
6433
      return tl::unexpected(pathname_component.error());
6434
    }
6435
    url_pattern_.pathname_component = std::move(*pathname_component);
6436
  } else {
6437
    // Otherwise set urlPattern's pathname component to the result of compiling
6438
    // a component given processedInit["pathname"], canonicalize an opaque
6439
    // pathname, and compileOptions.
6440
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6441
        processed_init->pathname.value(),
6442
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6443
    if (!pathname_component) {
6444
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6445
              processed_init->pathname.value());
6446
      return tl::unexpected(pathname_component.error());
6447
    }
6448
    url_pattern_.pathname_component = std::move(*pathname_component);
6449
  }
6450
6451
  // Set urlPattern's search component to the result of compiling a component
6452
  // given processedInit["search"], canonicalize a search, and compileOptions.
6453
  auto search_component = url_pattern_component<regex_provider>::compile(
6454
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6455
      compile_options);
6456
  if (!search_component) {
6457
    ada_log("url_pattern_component::compile failed for search ",
6458
            processed_init->search.value());
6459
    return tl::unexpected(search_component.error());
6460
  }
6461
  url_pattern_.search_component = std::move(*search_component);
6462
6463
  // Set urlPattern's hash component to the result of compiling a component
6464
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6465
  auto hash_component = url_pattern_component<regex_provider>::compile(
6466
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6467
      compile_options);
6468
  if (!hash_component) {
6469
    ada_log("url_pattern_component::compile failed for hash ",
6470
            processed_init->hash.value());
6471
    return tl::unexpected(hash_component.error());
6472
  }
6473
  url_pattern_.hash_component = std::move(*hash_component);
6474
6475
  // Return urlPattern.
6476
  return url_pattern_;
6477
}
6478
#endif  // ADA_INCLUDE_URL_PATTERN
6479
6480
}  // namespace ada::parser
6481
6482
#endif  // ADA_PARSER_INL_H
6483
/* end file include/ada/parser-inl.h */
6484
/* begin file include/ada/scheme-inl.h */
6485
/**
6486
 * @file scheme-inl.h
6487
 * @brief Definitions for the URL scheme.
6488
 */
6489
#ifndef ADA_SCHEME_INL_H
6490
#define ADA_SCHEME_INL_H
6491
6492
6493
namespace ada::scheme {
6494
6495
/**
6496
 * @namespace ada::scheme::details
6497
 * @brief Includes the definitions for scheme specific entities
6498
 */
6499
namespace details {
6500
// for use with is_special and get_special_port
6501
// Spaces, if present, are removed from URL.
6502
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6503
                                                "ftp",  "wss", "file",  " "};
6504
// for use with get_special_port
6505
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6506
6507
// @private
6508
// convert a string_view to a 64-bit integer key for fast comparison
6509
0
constexpr uint64_t make_key(std::string_view sv) {
6510
0
  uint64_t val = 0;
6511
0
  for (size_t i = 0; i < sv.size(); i++)
6512
0
    val |= (uint64_t)(uint8_t)sv[i] << (i * 8);
6513
0
  return val;
6514
0
}
6515
// precomputed keys for the special schemes, indexed by a hash of the input
6516
// string
6517
constexpr uint64_t scheme_keys[] = {
6518
    make_key("http"),   // 0: HTTP
6519
    0,                  // 1: sentinel
6520
    make_key("https"),  // 2: HTTPS
6521
    make_key("ws"),     // 3: WS
6522
    make_key("ftp"),    // 4: FTP
6523
    make_key("wss"),    // 5: WSS
6524
    make_key("file"),   // 6: FILE
6525
    0,                  // 7: sentinel
6526
};
6527
6528
// @private
6529
// branchless load of up to 5 characters into a uint64_t, padding with zeros if
6530
// n < 5
6531
2.60k
inline uint64_t branchless_load5(const char* p, size_t n) {
6532
2.60k
  uint64_t input = (uint8_t)p[0];
6533
2.60k
  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
6534
2.60k
  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
6535
2.60k
  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
6536
2.60k
  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
6537
2.60k
  return input;
6538
2.60k
}
6539
}  // namespace details
6540
6541
/****
6542
 * @private
6543
 * In is_special, get_scheme_type, and get_special_port, we
6544
 * use a standard hashing technique to find the index of the scheme in
6545
 * the is_special_list. The hashing technique is based on the size of
6546
 * the scheme and the first character of the scheme. It ensures that we
6547
 * do at most one string comparison per call. If the protocol is
6548
 * predictible (e.g., it is always "http"), we can get a better average
6549
 * performance by using a simpler approach where we loop and compare
6550
 * scheme with all possible protocols starting with the most likely
6551
 * protocol. Doing multiple comparisons may have a poor worst case
6552
 * performance, however. In this instance, we choose a potentially
6553
 * slightly lower best-case performance for a better worst-case
6554
 * performance. We can revisit this choice at any time.
6555
 *
6556
 * Reference:
6557
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6558
 * More C++ gems 17 (2000).
6559
 *
6560
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6561
 *
6562
 * Reference: https://github.com/ada-url/ada/issues/617
6563
 ****/
6564
6565
0
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6566
0
  if (scheme.empty()) {
6567
0
    return false;
6568
0
  }
6569
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6570
0
  const std::string_view target = details::is_special_list[hash_value];
6571
0
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6572
0
}
6573
0
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6574
0
  if (scheme.empty()) {
6575
0
    return 0;
6576
0
  }
6577
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6578
0
  const std::string_view target = details::is_special_list[hash_value];
6579
0
  if (scheme.size() == target.size() &&
6580
0
      details::branchless_load5(scheme.data(), scheme.size()) ==
6581
0
          details::scheme_keys[hash_value]) {
6582
0
    return details::special_ports[hash_value];
6583
0
  } else {
6584
0
    return 0;
6585
0
  }
6586
0
}
6587
0
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6588
0
  return details::special_ports[int(type)];
6589
0
}
6590
2.60k
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6591
2.60k
  if (scheme.empty()) {
6592
0
    return ada::scheme::NOT_SPECIAL;
6593
0
  }
6594
2.60k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6595
2.60k
  const std::string_view target = details::is_special_list[hash_value];
6596
2.60k
  if (scheme.size() == target.size() &&
6597
2.60k
      details::branchless_load5(scheme.data(), scheme.size()) ==
6598
2.60k
          details::scheme_keys[hash_value]) {
6599
2.60k
    return ada::scheme::type(hash_value);
6600
2.60k
  } else {
6601
0
    return ada::scheme::NOT_SPECIAL;
6602
0
  }
6603
2.60k
}
6604
6605
}  // namespace ada::scheme
6606
6607
#endif  // ADA_SCHEME_INL_H
6608
/* end file include/ada/scheme-inl.h */
6609
/* begin file include/ada/serializers.h */
6610
/**
6611
 * @file serializers.h
6612
 * @brief IP address serialization utilities.
6613
 *
6614
 * This header provides functions for converting IP addresses to their
6615
 * string representations according to the WHATWG URL Standard.
6616
 */
6617
#ifndef ADA_SERIALIZERS_H
6618
#define ADA_SERIALIZERS_H
6619
6620
6621
#include <array>
6622
#include <string>
6623
6624
/**
6625
 * @namespace ada::serializers
6626
 * @brief IP address serialization functions.
6627
 *
6628
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6629
 */
6630
namespace ada::serializers {
6631
6632
/**
6633
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6634
 * Used for :: compression in IPv6 serialization.
6635
 *
6636
 * @param address The 8 16-bit pieces of the IPv6 address.
6637
 * @param[out] compress Index of the start of the longest zero sequence.
6638
 * @param[out] compress_length Length of the longest zero sequence.
6639
 */
6640
void find_longest_sequence_of_ipv6_pieces(
6641
    const std::array<uint16_t, 8>& address, size_t& compress,
6642
    size_t& compress_length) noexcept;
6643
6644
/**
6645
 * Serializes an IPv6 address to its string representation.
6646
 *
6647
 * @param address The 8 16-bit pieces of the IPv6 address.
6648
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6649
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6650
 */
6651
std::string ipv6(const std::array<uint16_t, 8>& address);
6652
6653
/**
6654
 * Serializes an IPv4 address to its dotted-decimal string representation.
6655
 *
6656
 * @param address The 32-bit IPv4 address as an integer.
6657
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6658
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6659
 */
6660
std::string ipv4(uint64_t address);
6661
6662
}  // namespace ada::serializers
6663
6664
#endif  // ADA_SERIALIZERS_H
6665
/* end file include/ada/serializers.h */
6666
/* begin file include/ada/state.h */
6667
/**
6668
 * @file state.h
6669
 * @brief URL parser state machine states.
6670
 *
6671
 * Defines the states used by the URL parsing state machine as specified
6672
 * in the WHATWG URL Standard.
6673
 *
6674
 * @see https://url.spec.whatwg.org/#url-parsing
6675
 */
6676
#ifndef ADA_STATE_H
6677
#define ADA_STATE_H
6678
6679
6680
#include <string>
6681
6682
namespace ada {
6683
6684
/**
6685
 * @brief States in the URL parsing state machine.
6686
 *
6687
 * The URL parser processes input through a sequence of states, each handling
6688
 * a specific part of the URL syntax.
6689
 *
6690
 * @see https://url.spec.whatwg.org/#url-parsing
6691
 */
6692
enum class state {
6693
  /**
6694
   * @see https://url.spec.whatwg.org/#authority-state
6695
   */
6696
  AUTHORITY,
6697
6698
  /**
6699
   * @see https://url.spec.whatwg.org/#scheme-start-state
6700
   */
6701
  SCHEME_START,
6702
6703
  /**
6704
   * @see https://url.spec.whatwg.org/#scheme-state
6705
   */
6706
  SCHEME,
6707
6708
  /**
6709
   * @see https://url.spec.whatwg.org/#host-state
6710
   */
6711
  HOST,
6712
6713
  /**
6714
   * @see https://url.spec.whatwg.org/#no-scheme-state
6715
   */
6716
  NO_SCHEME,
6717
6718
  /**
6719
   * @see https://url.spec.whatwg.org/#fragment-state
6720
   */
6721
  FRAGMENT,
6722
6723
  /**
6724
   * @see https://url.spec.whatwg.org/#relative-state
6725
   */
6726
  RELATIVE_SCHEME,
6727
6728
  /**
6729
   * @see https://url.spec.whatwg.org/#relative-slash-state
6730
   */
6731
  RELATIVE_SLASH,
6732
6733
  /**
6734
   * @see https://url.spec.whatwg.org/#file-state
6735
   */
6736
  FILE,
6737
6738
  /**
6739
   * @see https://url.spec.whatwg.org/#file-host-state
6740
   */
6741
  FILE_HOST,
6742
6743
  /**
6744
   * @see https://url.spec.whatwg.org/#file-slash-state
6745
   */
6746
  FILE_SLASH,
6747
6748
  /**
6749
   * @see https://url.spec.whatwg.org/#path-or-authority-state
6750
   */
6751
  PATH_OR_AUTHORITY,
6752
6753
  /**
6754
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
6755
   */
6756
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
6757
6758
  /**
6759
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
6760
   */
6761
  SPECIAL_AUTHORITY_SLASHES,
6762
6763
  /**
6764
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
6765
   */
6766
  SPECIAL_RELATIVE_OR_AUTHORITY,
6767
6768
  /**
6769
   * @see https://url.spec.whatwg.org/#query-state
6770
   */
6771
  QUERY,
6772
6773
  /**
6774
   * @see https://url.spec.whatwg.org/#path-state
6775
   */
6776
  PATH,
6777
6778
  /**
6779
   * @see https://url.spec.whatwg.org/#path-start-state
6780
   */
6781
  PATH_START,
6782
6783
  /**
6784
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
6785
   */
6786
  OPAQUE_PATH,
6787
6788
  /**
6789
   * @see https://url.spec.whatwg.org/#port-state
6790
   */
6791
  PORT,
6792
};
6793
6794
/**
6795
 * Converts a parser state to its string name for debugging.
6796
 * @param s The state to convert.
6797
 * @return A string representation of the state.
6798
 */
6799
ada_warn_unused std::string to_string(ada::state s);
6800
6801
}  // namespace ada
6802
6803
#endif  // ADA_STATE_H
6804
/* end file include/ada/state.h */
6805
/* begin file include/ada/unicode.h */
6806
/**
6807
 * @file unicode.h
6808
 * @brief Definitions for all unicode specific functions.
6809
 */
6810
#ifndef ADA_UNICODE_H
6811
#define ADA_UNICODE_H
6812
6813
6814
#include <string>
6815
#include <string_view>
6816
#include <optional>
6817
6818
/**
6819
 * Unicode operations. These functions are not part of our public API and may
6820
 * change at any time.
6821
 *
6822
 * @private
6823
 * @namespace ada::unicode
6824
 * @brief Includes the definitions for unicode operations
6825
 */
6826
namespace ada::unicode {
6827
6828
/**
6829
 * @private
6830
 * We receive a UTF-8 string representing a domain name.
6831
 * If the string is percent encoded, we apply percent decoding.
6832
 *
6833
 * Given a domain, we need to identify its labels.
6834
 * They are separated by label-separators:
6835
 *
6836
 * U+002E (.) FULL STOP
6837
 * U+FF0E FULLWIDTH FULL STOP
6838
 * U+3002 IDEOGRAPHIC FULL STOP
6839
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
6840
 *
6841
 * They are all mapped to U+002E.
6842
 *
6843
 * We process each label into a string that should not exceed 63 octets.
6844
 * If the string is already punycode (starts with "xn--"), then we must
6845
 * scan it to look for unallowed code points.
6846
 * Otherwise, if the string is not pure ASCII, we need to transcode it
6847
 * to punycode by following RFC 3454 which requires us to
6848
 * - Map characters  (see section 3),
6849
 * - Normalize (see section 4),
6850
 * - Reject forbidden characters,
6851
 * - Check for right-to-left characters and if so, check all requirements (see
6852
 * section 6),
6853
 * - Optionally reject based on unassigned code points (section 7).
6854
 *
6855
 * The Unicode standard provides a table of code points with a mapping, a list
6856
 * of forbidden code points and so forth. This table is subject to change and
6857
 * will vary based on the implementation. For Unicode 15, the table is at
6858
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
6859
 * If you use ICU, they parse this table and map it to code using a Python
6860
 * script.
6861
 *
6862
 * The resulting strings should not exceed 255 octets according to RFC 1035
6863
 * section 2.3.4. ICU checks for label size and domain size, but these errors
6864
 * are ignored.
6865
 *
6866
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
6867
 *
6868
 */
6869
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
6870
              size_t first_percent);
6871
6872
/**
6873
 * @private
6874
 * Checks if the input has tab or newline characters.
6875
 *
6876
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
6877
 * enough that compilers like GCC can 'autovectorize it'.
6878
 */
6879
ada_really_inline bool has_tabs_or_newline(
6880
    std::string_view user_input) noexcept;
6881
6882
/**
6883
 * @private
6884
 * Checks if the input is a forbidden host code point.
6885
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
6886
 */
6887
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
6888
6889
/**
6890
 * @private
6891
 * Checks if the input contains a forbidden domain code point.
6892
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6893
 */
6894
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
6895
    const char* input, size_t length) noexcept;
6896
6897
/**
6898
 * @private
6899
 * Checks if the input contains a forbidden domain code point in which case
6900
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
6901
 * then the second bit is set to 1.
6902
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6903
 */
6904
ada_really_inline constexpr uint8_t
6905
contains_forbidden_domain_code_point_or_upper(const char* input,
6906
                                              size_t length) noexcept;
6907
6908
/**
6909
 * @private
6910
 * Checks if the input is a forbidden domain code point.
6911
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
6912
 */
6913
ada_really_inline constexpr bool is_forbidden_domain_code_point(
6914
    char c) noexcept;
6915
6916
/**
6917
 * @private
6918
 * Checks if the input is alphanumeric, '+', '-' or '.'
6919
 */
6920
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
6921
6922
/**
6923
 * @private
6924
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
6925
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
6926
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
6927
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
6928
 */
6929
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
6930
6931
/**
6932
 * @private
6933
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
6934
 * inclusive.
6935
 */
6936
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
6937
6938
/**
6939
 * @private
6940
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
6941
 * character.
6942
 */
6943
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
6944
6945
/**
6946
 * @private
6947
 * Checks if the input is a C0 control or space character.
6948
 *
6949
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
6950
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
6951
 * SEPARATOR ONE, inclusive.
6952
 */
6953
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
6954
6955
/**
6956
 * @private
6957
 * Checks if the input is a ASCII tab or newline character.
6958
 *
6959
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
6960
 */
6961
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
6962
6963
/**
6964
 * @private
6965
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
6966
 * match for ".%2e", "%2e.", or "%2e%2e".
6967
 */
6968
ada_really_inline constexpr bool is_double_dot_path_segment(
6969
    std::string_view input) noexcept;
6970
6971
/**
6972
 * @private
6973
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
6974
 * match for "%2e".
6975
 */
6976
ada_really_inline constexpr bool is_single_dot_path_segment(
6977
    std::string_view input) noexcept;
6978
6979
/**
6980
 * @private
6981
 * @details ipv4 character might contain 0-9 or a-f character ranges.
6982
 */
6983
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
6984
6985
/**
6986
 * @private
6987
 * @details Convert hex to binary. Caller is responsible to ensure that
6988
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
6989
 */
6990
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
6991
6992
/**
6993
 * @private
6994
 * first_percent should be  = input.find('%')
6995
 *
6996
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
6997
 * @author Node.js
6998
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
6999
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
7000
 */
7001
std::string percent_decode(std::string_view input, size_t first_percent);
7002
7003
/**
7004
 * @private
7005
 * Returns a percent-encoding string whether percent encoding was needed or not.
7006
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7007
 */
7008
std::string percent_encode(std::string_view input,
7009
                           const uint8_t character_set[]);
7010
/**
7011
 * @private
7012
 * Returns a percent-encoded string version of input, while starting the percent
7013
 * encoding at the provided index.
7014
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7015
 */
7016
std::string percent_encode(std::string_view input,
7017
                           const uint8_t character_set[], size_t index);
7018
/**
7019
 * @private
7020
 * Returns true if percent encoding was needed, in which case, we store
7021
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
7022
 * true, the content is appended to 'out'.
7023
 * If percent encoding is not needed, out is left unchanged.
7024
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7025
 */
7026
template <bool append>
7027
bool percent_encode(std::string_view input, const uint8_t character_set[],
7028
                    std::string& out);
7029
/**
7030
 * @private
7031
 * Returns the index at which percent encoding should start, or (equivalently),
7032
 * the length of the prefix that does not require percent encoding.
7033
 */
7034
ada_really_inline size_t percent_encode_index(std::string_view input,
7035
                                              const uint8_t character_set[]);
7036
/**
7037
 * @private
7038
 * Lowers the string in-place, assuming that the content is ASCII.
7039
 * Return true if the content was ASCII.
7040
 */
7041
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
7042
}  // namespace ada::unicode
7043
7044
#endif  // ADA_UNICODE_H
7045
/* end file include/ada/unicode.h */
7046
/* begin file include/ada/url_base-inl.h */
7047
/**
7048
 * @file url_base-inl.h
7049
 * @brief Inline functions for url base
7050
 */
7051
#ifndef ADA_URL_BASE_INL_H
7052
#define ADA_URL_BASE_INL_H
7053
7054
7055
#include <string>
7056
#if ADA_REGULAR_VISUAL_STUDIO
7057
#include <intrin.h>
7058
#endif  // ADA_REGULAR_VISUAL_STUDIO
7059
7060
namespace ada {
7061
7062
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
7063
17.0k
    const noexcept {
7064
17.0k
  return type != ada::scheme::NOT_SPECIAL;
7065
17.0k
}
7066
7067
0
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
7068
0
  return ada::scheme::get_special_port(type);
7069
0
}
7070
7071
[[nodiscard]] ada_really_inline uint16_t
7072
0
url_base::scheme_default_port() const noexcept {
7073
0
  return scheme::get_special_port(type);
7074
0
}
7075
7076
}  // namespace ada
7077
7078
#endif  // ADA_URL_BASE_INL_H
7079
/* end file include/ada/url_base-inl.h */
7080
/* begin file include/ada/url-inl.h */
7081
/**
7082
 * @file url-inl.h
7083
 * @brief Definitions for the URL
7084
 */
7085
#ifndef ADA_URL_INL_H
7086
#define ADA_URL_INL_H
7087
7088
7089
#include <charconv>
7090
#include <optional>
7091
#include <string>
7092
#if ADA_REGULAR_VISUAL_STUDIO
7093
#include <intrin.h>
7094
#endif  // ADA_REGULAR_VISUAL_STUDIO
7095
7096
namespace ada {
7097
0
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7098
0
  return !username.empty() || !password.empty();
7099
0
}
7100
0
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7101
0
  return port.has_value();
7102
0
}
7103
0
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7104
0
  return !host.has_value() || host->empty() || type == ada::scheme::type::FILE;
7105
0
}
7106
0
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7107
0
  if (!host.has_value()) {
7108
0
    return false;
7109
0
  }
7110
0
  return host->empty();
7111
0
}
7112
0
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7113
0
  return host.has_value();
7114
0
}
7115
0
inline std::ostream& operator<<(std::ostream& out, const ada::url& u) {
7116
0
  return out << u.to_string();
7117
0
}
7118
7119
0
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7120
0
  return path.size();
7121
0
}
7122
7123
0
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7124
0
  return path;
7125
0
}
7126
7127
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7128
0
    const {
7129
0
  url_components out{};
7130
0
7131
0
  // protocol ends with ':'. for example: "https:"
7132
0
  out.protocol_end = uint32_t(get_protocol().size());
7133
0
7134
0
  // Trailing index is always the next character of the current one.
7135
0
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7136
0
  size_t running_index = out.protocol_end;
7137
0
7138
0
  if (host.has_value()) {
7139
0
    // 2 characters for "//" and 1 character for starting index
7140
0
    out.host_start = out.protocol_end + 2;
7141
0
7142
0
    if (has_credentials()) {
7143
0
      out.username_end = uint32_t(out.host_start + username.size());
7144
0
7145
0
      out.host_start += uint32_t(username.size());
7146
0
7147
0
      if (!password.empty()) {
7148
0
        out.host_start += uint32_t(password.size() + 1);
7149
0
      }
7150
0
7151
0
      out.host_end = uint32_t(out.host_start + host->size());
7152
0
    } else {
7153
0
      out.username_end = out.host_start;
7154
0
7155
0
      // Host does not start with "@" if it does not include credentials.
7156
0
      out.host_end = uint32_t(out.host_start + host->size()) - 1;
7157
0
    }
7158
0
7159
0
    running_index = out.host_end + 1;
7160
0
  } else {
7161
0
    // Update host start and end date to the same index, since it does not
7162
0
    // exist.
7163
0
    out.host_start = out.protocol_end;
7164
0
    out.host_end = out.host_start;
7165
0
7166
0
    if (!has_opaque_path && path.starts_with("//")) {
7167
0
      // If url's host is null, url does not have an opaque path, url's path's
7168
0
      // size is greater than 1, and url's path[0] is the empty string, then
7169
0
      // append U+002F (/) followed by U+002E (.) to output.
7170
0
      running_index = out.protocol_end + 2;
7171
0
    } else {
7172
0
      running_index = out.protocol_end;
7173
0
    }
7174
0
  }
7175
0
7176
0
  if (port.has_value()) {
7177
0
    out.port = *port;
7178
0
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7179
0
  }
7180
0
7181
0
  out.pathname_start = uint32_t(running_index);
7182
0
7183
0
  running_index += path.size();
7184
0
7185
0
  if (query.has_value()) {
7186
0
    out.search_start = uint32_t(running_index);
7187
0
    running_index += get_search().size();
7188
0
    if (get_search().empty()) {
7189
0
      running_index++;
7190
0
    }
7191
0
  }
7192
0
7193
0
  if (hash.has_value()) {
7194
0
    out.hash_start = uint32_t(running_index);
7195
0
  }
7196
0
7197
0
  return out;
7198
0
}
7199
7200
0
inline void url::update_base_hostname(std::string_view input) { host = input; }
7201
7202
0
inline void url::update_unencoded_base_hash(std::string_view input) {
7203
  // We do the percent encoding
7204
0
  hash = unicode::percent_encode(input,
7205
0
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7206
0
}
7207
7208
inline void url::update_base_search(std::string_view input,
7209
0
                                    const uint8_t query_percent_encode_set[]) {
7210
0
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7211
0
}
7212
7213
0
inline void url::update_base_search(std::optional<std::string>&& input) {
7214
0
  query = std::move(input);
7215
0
}
7216
7217
0
inline void url::update_base_pathname(const std::string_view input) {
7218
0
  path = input;
7219
0
}
7220
7221
0
inline void url::update_base_username(const std::string_view input) {
7222
0
  username = input;
7223
0
}
7224
7225
0
inline void url::update_base_password(const std::string_view input) {
7226
0
  password = input;
7227
0
}
7228
7229
0
inline void url::update_base_port(std::optional<uint16_t> input) {
7230
0
  port = input;
7231
0
}
7232
7233
0
constexpr void url::clear_pathname() { path.clear(); }
7234
7235
0
constexpr void url::clear_search() { query = std::nullopt; }
7236
7237
0
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7238
0
  return hash.has_value();
7239
0
}
7240
7241
0
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7242
0
  return query.has_value();
7243
0
}
7244
7245
0
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7246
7247
0
inline void url::set_scheme(std::string&& new_scheme) noexcept {
7248
0
  type = ada::scheme::get_scheme_type(new_scheme);
7249
  // We only move the 'scheme' if it is non-special.
7250
0
  if (!is_special()) {
7251
0
    non_special_scheme = std::move(new_scheme);
7252
0
  }
7253
0
}
7254
7255
0
constexpr void url::copy_scheme(ada::url&& u) {
7256
0
  non_special_scheme = u.non_special_scheme;
7257
0
  type = u.type;
7258
0
}
7259
7260
0
constexpr void url::copy_scheme(const ada::url& u) {
7261
0
  non_special_scheme = u.non_special_scheme;
7262
0
  type = u.type;
7263
0
}
7264
7265
0
[[nodiscard]] ada_really_inline std::string url::get_href() const {
7266
0
  std::string output = get_protocol();
7267
0
7268
0
  if (host.has_value()) {
7269
0
    output += "//";
7270
0
    if (has_credentials()) {
7271
0
      output += username;
7272
0
      if (!password.empty()) {
7273
0
        output += ":" + get_password();
7274
0
      }
7275
0
      output += "@";
7276
0
    }
7277
0
    output += host.value();
7278
0
    if (port.has_value()) {
7279
0
      output += ":" + get_port();
7280
0
    }
7281
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7282
0
    // If url's host is null, url does not have an opaque path, url's path's
7283
0
    // size is greater than 1, and url's path[0] is the empty string, then
7284
0
    // append U+002F (/) followed by U+002E (.) to output.
7285
0
    output += "/.";
7286
0
  }
7287
0
  output += path;
7288
0
  if (query.has_value()) {
7289
0
    output += "?" + query.value();
7290
0
  }
7291
0
  if (hash.has_value()) {
7292
0
    output += "#" + hash.value();
7293
0
  }
7294
0
  return output;
7295
0
}
7296
7297
0
[[nodiscard]] inline size_t url::get_href_size() const noexcept {
7298
  // Mirrors the logic of get_href() but only computes the total size.
7299
0
  size_t size = 0;
7300
  // Protocol: scheme + ":"
7301
0
  if (is_special()) {
7302
0
    size += ada::scheme::details::is_special_list[type].size() + 1;
7303
0
  } else {
7304
0
    size += non_special_scheme.size() + 1;
7305
0
  }
7306
0
  if (host.has_value()) {
7307
0
    size += host->size();
7308
0
    size += 2;  // "//"
7309
0
    if (has_credentials()) {
7310
0
      size += username.size();
7311
0
      if (!password.empty()) {
7312
0
        size += 1 + password.size();  // ":" + password
7313
0
      }
7314
0
      size += 1;  // "@"
7315
0
    }
7316
0
    if (port.has_value()) {
7317
0
      size += 1;  // ":"
7318
      // Count digits of port value without calling std::to_string.
7319
0
      uint16_t p = *port;
7320
0
      size += (p >= 10000)  ? 5
7321
0
              : (p >= 1000) ? 4
7322
0
              : (p >= 100)  ? 3
7323
0
              : (p >= 10)   ? 2
7324
0
                            : 1;
7325
0
    }
7326
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7327
0
    size += 2;  // "/."
7328
0
  }
7329
0
  size += path.size();
7330
0
  if (query.has_value()) {
7331
0
    size += 1 + query->size();  // "?" + query
7332
0
  }
7333
0
  if (hash.has_value()) {
7334
0
    size += 1 + hash->size();  // "#" + hash
7335
0
  }
7336
0
  return size;
7337
0
}
7338
7339
ada_really_inline size_t url::parse_port(std::string_view view,
7340
0
                                         bool check_trailing_content) noexcept {
7341
0
  ada_log("parse_port('", view, "') ", view.size());
7342
0
  if (!view.empty() && view[0] == '-') {
7343
0
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7344
0
    is_valid = false;
7345
0
    return 0;
7346
0
  }
7347
0
  uint16_t parsed_port{};
7348
0
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7349
0
  if (r.ec == std::errc::result_out_of_range) {
7350
0
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7351
0
    is_valid = false;
7352
0
    return 0;
7353
0
  }
7354
0
  ada_log("parse_port: ", parsed_port);
7355
0
  const auto consumed = size_t(r.ptr - view.data());
7356
0
  ada_log("parse_port: consumed ", consumed);
7357
0
  if (check_trailing_content) {
7358
0
    is_valid &=
7359
0
        (consumed == view.size() || view[consumed] == '/' ||
7360
0
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7361
0
  }
7362
0
  ada_log("parse_port: is_valid = ", is_valid);
7363
0
  if (is_valid) {
7364
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7365
0
    auto default_port = scheme_default_port();
7366
0
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7367
0
                         (default_port != parsed_port);
7368
0
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7369
0
                                                  : std::nullopt;
7370
0
  }
7371
0
  return consumed;
7372
0
}
7373
7374
}  // namespace ada
7375
7376
#endif  // ADA_URL_H
7377
/* end file include/ada/url-inl.h */
7378
/* begin file include/ada/url_components-inl.h */
7379
/**
7380
 * @file url_components.h
7381
 * @brief Declaration for the URL Components
7382
 */
7383
#ifndef ADA_URL_COMPONENTS_INL_H
7384
#define ADA_URL_COMPONENTS_INL_H
7385
7386
7387
namespace ada {
7388
7389
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7390
2.60k
    const noexcept {
7391
  /**
7392
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7393
   *       |     |    |          | ^^^^|       |   |
7394
   *       |     |    |          | |   |       |   `----- hash_start
7395
   *       |     |    |          | |   |       `--------- search_start
7396
   *       |     |    |          | |   `----------------- pathname_start
7397
   *       |     |    |          | `--------------------- port
7398
   *       |     |    |          `----------------------- host_end
7399
   *       |     |    `---------------------------------- host_start
7400
   *       |     `--------------------------------------- username_end
7401
   *       `--------------------------------------------- protocol_end
7402
   */
7403
  // These conditions can be made more strict.
7404
2.60k
  if (protocol_end == url_components::omitted) {
7405
0
    return false;
7406
0
  }
7407
2.60k
  uint32_t index = protocol_end;
7408
7409
2.60k
  if (username_end == url_components::omitted) {
7410
0
    return false;
7411
0
  }
7412
2.60k
  if (username_end < index) {
7413
0
    return false;
7414
0
  }
7415
2.60k
  index = username_end;
7416
7417
2.60k
  if (host_start == url_components::omitted) {
7418
0
    return false;
7419
0
  }
7420
2.60k
  if (host_start < index) {
7421
0
    return false;
7422
0
  }
7423
2.60k
  index = host_start;
7424
7425
2.60k
  if (port != url_components::omitted) {
7426
0
    if (port > 0xffff) {
7427
0
      return false;
7428
0
    }
7429
0
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7430
0
    if (index + port_length < index) {
7431
0
      return false;
7432
0
    }
7433
0
    index += port_length;
7434
0
  }
7435
7436
2.60k
  if (pathname_start == url_components::omitted) {
7437
0
    return false;
7438
0
  }
7439
2.60k
  if (pathname_start < index) {
7440
0
    return false;
7441
0
  }
7442
2.60k
  index = pathname_start;
7443
7444
2.60k
  if (search_start != url_components::omitted) {
7445
1.37k
    if (search_start < index) {
7446
0
      return false;
7447
0
    }
7448
1.37k
    index = search_start;
7449
1.37k
  }
7450
7451
2.60k
  if (hash_start != url_components::omitted) {
7452
160
    if (hash_start < index) {
7453
0
      return false;
7454
0
    }
7455
160
  }
7456
7457
2.60k
  return true;
7458
2.60k
}
7459
7460
}  // namespace ada
7461
#endif
7462
/* end file include/ada/url_components-inl.h */
7463
/* begin file include/ada/url_aggregator.h */
7464
/**
7465
 * @file url_aggregator.h
7466
 * @brief Declaration for the `ada::url_aggregator` class.
7467
 *
7468
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7469
 * URL using a single buffer with component offsets. This is the default and
7470
 * most memory-efficient URL representation in Ada.
7471
 *
7472
 * @see url.h for an alternative representation using separate strings
7473
 */
7474
#ifndef ADA_URL_AGGREGATOR_H
7475
#define ADA_URL_AGGREGATOR_H
7476
7477
#include <ostream>
7478
#include <string>
7479
#include <string_view>
7480
#include <variant>
7481
7482
7483
namespace ada {
7484
7485
namespace parser {}
7486
7487
/**
7488
 * @brief Memory-efficient URL representation using a single buffer.
7489
 *
7490
 * The `url_aggregator` stores the entire normalized URL in a single string
7491
 * buffer and tracks component boundaries using offsets. This design minimizes
7492
 * memory allocations and is ideal for read-mostly access patterns.
7493
 *
7494
 * Getter methods return `std::string_view` pointing into the internal buffer.
7495
 * These views are lightweight (no allocation) but become invalid if the
7496
 * url_aggregator is modified or destroyed.
7497
 *
7498
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7499
 * when any setter is called. Do not use a getter's result as input to a
7500
 * setter on the same object without copying first.
7501
 *
7502
 * @note This is the default URL type returned by `ada::parse()`.
7503
 *
7504
 * @see url For an alternative using separate std::string instances
7505
 */
7506
struct url_aggregator : url_base {
7507
2.60k
  url_aggregator() = default;
7508
1.37k
  url_aggregator(const url_aggregator& u) = default;
7509
2.60k
  url_aggregator(url_aggregator&& u) noexcept = default;
7510
0
  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
7511
0
  url_aggregator& operator=(const url_aggregator& u) = default;
7512
6.58k
  ~url_aggregator() override = default;
7513
7514
  /**
7515
   * The setter functions follow the steps defined in the URL Standard.
7516
   *
7517
   * The url_aggregator has a single buffer that contains the entire normalized
7518
   * URL. The various components are represented as offsets into that buffer.
7519
   * When you call get_pathname(), for example, you get a std::string_view that
7520
   * points into that buffer. If the url_aggregator is modified, the buffer may
7521
   * be reallocated, and the std::string_view you obtained earlier may become
7522
   * invalid. In particular, this implies that you cannot modify the URL using
7523
   * a setter function with a std::string_view that points into the
7524
   * url_aggregator E.g., the following is incorrect:
7525
   * url->set_hostname(url->get_pathname()).
7526
   * You must first copy the pathname to a separate string.
7527
   * std::string pathname(url->get_pathname());
7528
   * url->set_hostname(pathname);
7529
   *
7530
   * The caller is responsible for ensuring that the url_aggregator is not
7531
   * modified while any std::string_view obtained from it is in use.
7532
   */
7533
  bool set_href(std::string_view input);
7534
  bool set_host(std::string_view input);
7535
  bool set_hostname(std::string_view input);
7536
  bool set_protocol(std::string_view input);
7537
  bool set_username(std::string_view input);
7538
  bool set_password(std::string_view input);
7539
  bool set_port(std::string_view input);
7540
  bool set_pathname(std::string_view input);
7541
  void set_search(std::string_view input);
7542
  void set_hash(std::string_view input);
7543
7544
  /**
7545
   * Validates whether the hostname is a valid domain according to RFC 1034.
7546
   * @return `true` if the domain is valid, `false` otherwise.
7547
   */
7548
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7549
7550
  /**
7551
   * Returns the URL's origin (scheme + host + port for special URLs).
7552
   * @return A newly allocated string containing the serialized origin.
7553
   * @see https://url.spec.whatwg.org/#concept-url-origin
7554
   */
7555
  [[nodiscard]] std::string get_origin() const override;
7556
7557
  /**
7558
   * Returns the full serialized URL (the href) as a string_view.
7559
   * Does not allocate memory. The returned view becomes invalid if this
7560
   * url_aggregator is modified or destroyed.
7561
   * @return A string_view into the internal buffer.
7562
   * @see https://url.spec.whatwg.org/#dom-url-href
7563
   */
7564
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7565
      ada_lifetime_bound;
7566
7567
  /**
7568
   * Returns the byte length of the serialized URL without allocating a string.
7569
   * @return Size of the href in bytes.
7570
   */
7571
  [[nodiscard]] constexpr size_t get_href_size() const noexcept;
7572
7573
  /**
7574
   * Returns the URL's username component.
7575
   * Does not allocate memory. The returned view becomes invalid if this
7576
   * url_aggregator is modified or destroyed.
7577
   * @return A string_view of the username.
7578
   * @see https://url.spec.whatwg.org/#dom-url-username
7579
   */
7580
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
7581
7582
  /**
7583
   * Returns the URL's password component.
7584
   * Does not allocate memory. The returned view becomes invalid if this
7585
   * url_aggregator is modified or destroyed.
7586
   * @return A string_view of the password.
7587
   * @see https://url.spec.whatwg.org/#dom-url-password
7588
   */
7589
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
7590
7591
  /**
7592
   * Returns the URL's port as a string (e.g., "8080").
7593
   * Does not allocate memory. Returns empty view if no port is set.
7594
   * The returned view becomes invalid if this url_aggregator is modified.
7595
   * @return A string_view of the port.
7596
   * @see https://url.spec.whatwg.org/#dom-url-port
7597
   */
7598
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
7599
7600
  /**
7601
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
7602
   * Does not allocate memory. Returns empty view if no fragment is set.
7603
   * The returned view becomes invalid if this url_aggregator is modified.
7604
   * @return A string_view of the hash.
7605
   * @see https://url.spec.whatwg.org/#dom-url-hash
7606
   */
7607
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
7608
7609
  /**
7610
   * Returns the URL's host and port (e.g., "example.com:8080").
7611
   * Does not allocate memory. Returns empty view if no host is set.
7612
   * The returned view becomes invalid if this url_aggregator is modified.
7613
   * @return A string_view of host:port.
7614
   * @see https://url.spec.whatwg.org/#dom-url-host
7615
   */
7616
  [[nodiscard]] std::string_view get_host() const ada_lifetime_bound;
7617
7618
  /**
7619
   * Returns the URL's hostname (without port).
7620
   * Does not allocate memory. Returns empty view if no host is set.
7621
   * The returned view becomes invalid if this url_aggregator is modified.
7622
   * @return A string_view of the hostname.
7623
   * @see https://url.spec.whatwg.org/#dom-url-hostname
7624
   */
7625
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
7626
7627
  /**
7628
   * Returns the URL's path component.
7629
   * Does not allocate memory. The returned view becomes invalid if this
7630
   * url_aggregator is modified or destroyed.
7631
   * @return A string_view of the pathname.
7632
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7633
   */
7634
  [[nodiscard]] constexpr std::string_view get_pathname() const
7635
      ada_lifetime_bound;
7636
7637
  /**
7638
   * Returns the byte length of the pathname without creating a string.
7639
   * @return Size of the pathname in bytes.
7640
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7641
   */
7642
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
7643
7644
  /**
7645
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
7646
   * Does not allocate memory. Returns empty view if no query is set.
7647
   * The returned view becomes invalid if this url_aggregator is modified.
7648
   * @return A string_view of the search/query.
7649
   * @see https://url.spec.whatwg.org/#dom-url-search
7650
   */
7651
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
7652
7653
  /**
7654
   * Returns the URL's scheme followed by a colon (e.g., "https:").
7655
   * Does not allocate memory. The returned view becomes invalid if this
7656
   * url_aggregator is modified or destroyed.
7657
   * @return A string_view of the protocol.
7658
   * @see https://url.spec.whatwg.org/#dom-url-protocol
7659
   */
7660
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
7661
7662
  /**
7663
   * Checks if the URL has credentials (non-empty username or password).
7664
   * @return `true` if username or password is non-empty, `false` otherwise.
7665
   */
7666
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
7667
      const noexcept;
7668
7669
  /**
7670
   * Returns the URL component offsets for efficient serialization.
7671
   *
7672
   * The components represent byte offsets into the serialized URL:
7673
   * ```
7674
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7675
   *       |     |    |          | ^^^^|       |   |
7676
   *       |     |    |          | |   |       |   `----- hash_start
7677
   *       |     |    |          | |   |       `--------- search_start
7678
   *       |     |    |          | |   `----------------- pathname_start
7679
   *       |     |    |          | `--------------------- port
7680
   *       |     |    |          `----------------------- host_end
7681
   *       |     |    `---------------------------------- host_start
7682
   *       |     `--------------------------------------- username_end
7683
   *       `--------------------------------------------- protocol_end
7684
   * ```
7685
   * @return A constant reference to the url_components struct.
7686
   * @see https://github.com/servo/rust-url
7687
   */
7688
  [[nodiscard]] ada_really_inline const url_components& get_components()
7689
      const noexcept;
7690
7691
  /**
7692
   * Returns a JSON string representation of this URL for debugging.
7693
   * @return A JSON-formatted string with all URL components.
7694
   */
7695
  [[nodiscard]] std::string to_string() const override;
7696
7697
  /**
7698
   * Returns a visual diagram showing component boundaries in the URL.
7699
   * Useful for debugging and understanding URL structure.
7700
   * @return A multi-line string diagram.
7701
   */
7702
  [[nodiscard]] std::string to_diagram() const;
7703
7704
  /**
7705
   * Validates internal consistency of component offsets (for debugging).
7706
   * @return `true` if offsets are consistent, `false` if corrupted.
7707
   */
7708
  [[nodiscard]] constexpr bool validate() const noexcept;
7709
7710
  /**
7711
   * Checks if the URL has an empty hostname (host is set but empty string).
7712
   * @return `true` if host exists but is empty, `false` otherwise.
7713
   */
7714
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
7715
7716
  /**
7717
   * Checks if the URL has a hostname (including empty hostnames).
7718
   * @return `true` if host is present, `false` otherwise.
7719
   */
7720
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
7721
7722
  /**
7723
   * Checks if the URL has a non-empty username.
7724
   * @return `true` if username is non-empty, `false` otherwise.
7725
   */
7726
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
7727
7728
  /**
7729
   * Checks if the URL has a non-empty password.
7730
   * @return `true` if password is non-empty, `false` otherwise.
7731
   */
7732
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
7733
7734
  /**
7735
   * Checks if the URL has a non-default port explicitly specified.
7736
   * @return `true` if a port is present, `false` otherwise.
7737
   */
7738
  [[nodiscard]] constexpr bool has_port() const noexcept;
7739
7740
  /**
7741
   * Checks if the URL has a password component (may be empty).
7742
   * @return `true` if password is present, `false` otherwise.
7743
   */
7744
  [[nodiscard]] constexpr bool has_password() const noexcept;
7745
7746
  /**
7747
   * Checks if the URL has a fragment/hash component.
7748
   * @return `true` if hash is present, `false` otherwise.
7749
   */
7750
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
7751
7752
  /**
7753
   * Checks if the URL has a query/search component.
7754
   * @return `true` if query is present, `false` otherwise.
7755
   */
7756
  [[nodiscard]] constexpr bool has_search() const noexcept override;
7757
7758
  /**
7759
   * Removes the port from the URL.
7760
   */
7761
  inline void clear_port();
7762
7763
  /**
7764
   * Removes the hash/fragment from the URL.
7765
   */
7766
  inline void clear_hash();
7767
7768
  /**
7769
   * Removes the query/search string from the URL.
7770
   */
7771
  inline void clear_search() override;
7772
7773
 private:
7774
  // helper methods
7775
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
7776
      url_aggregator& url);
7777
  // parse_url methods
7778
  friend url_aggregator parser::parse_url<url_aggregator>(
7779
      std::string_view, const url_aggregator*);
7780
7781
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
7782
      std::string_view, const url_aggregator*);
7783
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
7784
      std::string_view, const url_aggregator*);
7785
7786
#if ADA_INCLUDE_URL_PATTERN
7787
  // url_pattern methods
7788
  template <url_pattern_regex::regex_concept regex_provider>
7789
  friend tl::expected<url_pattern<regex_provider>, errors>
7790
  parse_url_pattern_impl(
7791
      std::variant<std::string_view, url_pattern_init>&& input,
7792
      const std::string_view* base_url, const url_pattern_options* options);
7793
#endif  // ADA_INCLUDE_URL_PATTERN
7794
7795
  // components is declared before buffer so that the offset fields land
7796
  // close to url_base in memory, improving cache locality for getter calls.
7797
  // Note: exact cache-line placement is implementation- and platform-dependent.
7798
  url_components components{};
7799
  std::string buffer{};
7800
7801
  /**
7802
   * Returns true if neither the search, nor the hash nor the pathname
7803
   * have been set.
7804
   * @return true if the buffer is ready to receive the path.
7805
   */
7806
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
7807
7808
  inline void add_authority_slashes_if_needed();
7809
7810
  /**
7811
   * To optimize performance, you may indicate how much memory to allocate
7812
   * within this instance.
7813
   */
7814
  constexpr void reserve(uint32_t capacity);
7815
7816
  ada_really_inline size_t parse_port(std::string_view view,
7817
                                      bool check_trailing_content) override;
7818
7819
0
  ada_really_inline size_t parse_port(std::string_view view) override {
7820
0
    return this->parse_port(view, false);
7821
0
  }
7822
7823
  /**
7824
   * Return true on success. The 'in_place' parameter indicates whether the
7825
   * the string_view input is pointing in the buffer. When in_place is false,
7826
   * we must nearly always update the buffer.
7827
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
7828
   */
7829
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
7830
7831
  /**
7832
   * Return true on success.
7833
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
7834
   */
7835
  [[nodiscard]] bool parse_ipv6(std::string_view input);
7836
7837
  /**
7838
   * Return true on success.
7839
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
7840
   */
7841
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
7842
7843
  ada_really_inline void parse_path(std::string_view input);
7844
7845
  /**
7846
   * A URL cannot have a username/password/port if its host is null or the empty
7847
   * string, or its scheme is "file".
7848
   */
7849
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
7850
7851
  template <bool override_hostname = false>
7852
  bool set_host_or_hostname(std::string_view input);
7853
7854
  ada_really_inline bool parse_host(std::string_view input);
7855
7856
  inline void update_base_authority(std::string_view base_buffer,
7857
                                    const url_components& base);
7858
  inline void update_unencoded_base_hash(std::string_view input);
7859
  inline void update_base_hostname(std::string_view input);
7860
  inline void update_base_search(std::string_view input);
7861
  inline void update_base_search(std::string_view input,
7862
                                 const uint8_t* query_percent_encode_set);
7863
  inline void update_base_pathname(std::string_view input);
7864
  inline void update_base_username(std::string_view input);
7865
  inline void append_base_username(std::string_view input);
7866
  inline void update_base_password(std::string_view input);
7867
  inline void append_base_password(std::string_view input);
7868
  inline void update_base_port(uint32_t input);
7869
  inline void append_base_pathname(std::string_view input);
7870
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
7871
  constexpr void clear_hostname();
7872
  constexpr void clear_password();
7873
  constexpr void clear_pathname() override;
7874
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
7875
  void delete_dash_dot();
7876
  inline void consume_prepared_path(std::string_view input);
7877
  template <bool has_state_override = false>
7878
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
7879
      std::string_view input);
7880
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
7881
                                                std::string_view input);
7882
  [[nodiscard]] constexpr bool has_authority() const noexcept;
7883
  constexpr void set_protocol_as_file();
7884
  inline void set_scheme(std::string_view new_scheme);
7885
  /**
7886
   * Fast function to set the scheme from a view with a colon in the
7887
   * buffer, does not change type.
7888
   */
7889
  inline void set_scheme_from_view_with_colon(
7890
      std::string_view new_scheme_with_colon);
7891
  inline void copy_scheme(const url_aggregator& u);
7892
7893
  inline void update_host_to_base_host(const std::string_view input);
7894
7895
};  // url_aggregator
7896
7897
inline std::ostream& operator<<(std::ostream& out, const url& u);
7898
}  // namespace ada
7899
7900
#endif
7901
/* end file include/ada/url_aggregator.h */
7902
/* begin file include/ada/url_aggregator-inl.h */
7903
/**
7904
 * @file url_aggregator-inl.h
7905
 * @brief Inline functions for url aggregator
7906
 */
7907
#ifndef ADA_URL_AGGREGATOR_INL_H
7908
#define ADA_URL_AGGREGATOR_INL_H
7909
7910
/* begin file include/ada/unicode-inl.h */
7911
/**
7912
 * @file unicode-inl.h
7913
 * @brief Definitions for unicode operations.
7914
 */
7915
#ifndef ADA_UNICODE_INL_H
7916
#define ADA_UNICODE_INL_H
7917
7918
/**
7919
 * Unicode operations. These functions are not part of our public API and may
7920
 * change at any time.
7921
 *
7922
 * private
7923
 * @namespace ada::unicode
7924
 * @brief Includes the declarations for unicode operations
7925
 */
7926
namespace ada::unicode {
7927
ada_really_inline size_t percent_encode_index(const std::string_view input,
7928
116
                                              const uint8_t character_set[]) {
7929
116
  const char* data = input.data();
7930
116
  const size_t size = input.size();
7931
7932
  // Process 8 bytes at a time using unrolled loop
7933
116
  size_t i = 0;
7934
2.20k
  for (; i + 8 <= size; i += 8) {
7935
2.09k
    unsigned char chunk[8];
7936
2.09k
    std::memcpy(&chunk, data + i,
7937
2.09k
                8);  // entices compiler to unconditionally process 8 characters
7938
7939
    // Check 8 characters at once
7940
18.8k
    for (size_t j = 0; j < 8; j++) {
7941
16.7k
      if (character_sets::bit_at(character_set, chunk[j])) {
7942
0
        return i + j;
7943
0
      }
7944
16.7k
    }
7945
2.09k
  }
7946
7947
  // Handle remaining bytes
7948
553
  for (; i < size; i++) {
7949
437
    if (character_sets::bit_at(character_set, data[i])) {
7950
0
      return i;
7951
0
    }
7952
437
  }
7953
7954
116
  return size;
7955
116
}
7956
}  // namespace ada::unicode
7957
7958
#endif  // ADA_UNICODE_INL_H
7959
/* end file include/ada/unicode-inl.h */
7960
7961
#include <charconv>
7962
#include <ostream>
7963
#include <string_view>
7964
7965
namespace ada {
7966
7967
inline void url_aggregator::update_base_authority(
7968
0
    std::string_view base_buffer, const ada::url_components& base) {
7969
0
  std::string_view input = base_buffer.substr(
7970
0
      base.protocol_end, base.host_start - base.protocol_end);
7971
0
  ada_log("url_aggregator::update_base_authority ", input);
7972
7973
0
  bool input_starts_with_dash = input.starts_with("//");
7974
0
  uint32_t diff = components.host_start - components.protocol_end;
7975
7976
0
  buffer.erase(components.protocol_end,
7977
0
               components.host_start - components.protocol_end);
7978
0
  components.username_end = components.protocol_end;
7979
7980
0
  if (input_starts_with_dash) {
7981
0
    input.remove_prefix(2);
7982
0
    diff += 2;  // add "//"
7983
0
    buffer.insert(components.protocol_end, "//");
7984
0
    components.username_end += 2;
7985
0
  }
7986
7987
0
  size_t password_delimiter = input.find(':');
7988
7989
  // Check if input contains both username and password by checking the
7990
  // delimiter: ":" A typical input that contains authority would be "user:pass"
7991
0
  if (password_delimiter != std::string_view::npos) {
7992
    // Insert both username and password
7993
0
    std::string_view username = input.substr(0, password_delimiter);
7994
0
    std::string_view password = input.substr(password_delimiter + 1);
7995
7996
0
    buffer.insert(components.protocol_end + diff, username);
7997
0
    diff += uint32_t(username.size());
7998
0
    buffer.insert(components.protocol_end + diff, ":");
7999
0
    components.username_end = components.protocol_end + diff;
8000
0
    buffer.insert(components.protocol_end + diff + 1, password);
8001
0
    diff += uint32_t(password.size()) + 1;
8002
0
  } else if (!input.empty()) {
8003
    // Insert only username
8004
0
    buffer.insert(components.protocol_end + diff, input);
8005
0
    components.username_end =
8006
0
        components.protocol_end + diff + uint32_t(input.size());
8007
0
    diff += uint32_t(input.size());
8008
0
  }
8009
8010
0
  components.host_start += diff;
8011
8012
0
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
8013
0
    buffer.insert(components.host_start, "@");
8014
0
    diff++;
8015
0
  }
8016
0
  components.host_end += diff;
8017
0
  components.pathname_start += diff;
8018
0
  if (components.search_start != url_components::omitted) {
8019
0
    components.search_start += diff;
8020
0
  }
8021
0
  if (components.hash_start != url_components::omitted) {
8022
0
    components.hash_start += diff;
8023
0
  }
8024
0
}
8025
8026
160
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
8027
160
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
8028
160
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
8029
160
          " bytes] components.hash_start = ", components.hash_start);
8030
160
  ADA_ASSERT_TRUE(validate());
8031
160
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8032
160
  if (components.hash_start != url_components::omitted) {
8033
0
    buffer.resize(components.hash_start);
8034
0
  }
8035
160
  components.hash_start = uint32_t(buffer.size());
8036
160
  buffer += "#";
8037
160
  bool encoding_required = unicode::percent_encode<true>(
8038
160
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
8039
  // When encoding_required is false, then buffer is left unchanged, and percent
8040
  // encoding was not deemed required.
8041
160
  if (!encoding_required) {
8042
108
    buffer.append(input);
8043
108
  }
8044
160
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
8045
160
          buffer, "' [", buffer.size(), " bytes]");
8046
160
  ADA_ASSERT_TRUE(validate());
8047
160
}
8048
8049
ada_really_inline uint32_t url_aggregator::replace_and_resize(
8050
2.60k
    uint32_t start, uint32_t end, std::string_view input) {
8051
2.60k
  uint32_t current_length = end - start;
8052
2.60k
  uint32_t input_size = uint32_t(input.size());
8053
2.60k
  uint32_t new_difference = input_size - current_length;
8054
8055
2.60k
  if (current_length == 0) {
8056
2.60k
    buffer.insert(start, input);
8057
2.60k
  } else if (input_size == current_length) {
8058
0
    buffer.replace(start, input_size, input);
8059
0
  } else if (input_size < current_length) {
8060
0
    buffer.erase(start, current_length - input_size);
8061
0
    buffer.replace(start, input_size, input);
8062
0
  } else {
8063
0
    buffer.replace(start, current_length, input.substr(0, current_length));
8064
0
    buffer.insert(start + current_length, input.substr(current_length));
8065
0
  }
8066
8067
2.60k
  return new_difference;
8068
2.60k
}
8069
8070
2.60k
inline void url_aggregator::update_base_hostname(const std::string_view input) {
8071
2.60k
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
8072
2.60k
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
8073
2.60k
  ADA_ASSERT_TRUE(validate());
8074
2.60k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8075
8076
  // This next line is required for when parsing a URL like `foo://`
8077
2.60k
  add_authority_slashes_if_needed();
8078
8079
2.60k
  bool has_credentials = components.protocol_end + 2 < components.host_start;
8080
2.60k
  uint32_t new_difference =
8081
2.60k
      replace_and_resize(components.host_start, components.host_end, input);
8082
8083
2.60k
  if (has_credentials) {
8084
0
    buffer.insert(components.host_start, "@");
8085
0
    new_difference++;
8086
0
  }
8087
2.60k
  components.host_end += new_difference;
8088
2.60k
  components.pathname_start += new_difference;
8089
2.60k
  if (components.search_start != url_components::omitted) {
8090
0
    components.search_start += new_difference;
8091
0
  }
8092
2.60k
  if (components.hash_start != url_components::omitted) {
8093
0
    components.hash_start += new_difference;
8094
0
  }
8095
2.60k
  ADA_ASSERT_TRUE(validate());
8096
2.60k
}
8097
8098
[[nodiscard]] ada_really_inline uint32_t
8099
0
url_aggregator::get_pathname_length() const noexcept {
8100
0
  ada_log("url_aggregator::get_pathname_length");
8101
0
  uint32_t ending_index = uint32_t(buffer.size());
8102
0
  if (components.search_start != url_components::omitted) {
8103
0
    ending_index = components.search_start;
8104
0
  } else if (components.hash_start != url_components::omitted) {
8105
0
    ending_index = components.hash_start;
8106
0
  }
8107
0
  return ending_index - components.pathname_start;
8108
0
}
8109
8110
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
8111
2.60k
    const noexcept {
8112
2.60k
  return buffer.size() == components.pathname_start;
8113
2.60k
}
8114
8115
0
inline void url_aggregator::update_base_search(std::string_view input) {
8116
0
  ada_log("url_aggregator::update_base_search ", input);
8117
0
  ADA_ASSERT_TRUE(validate());
8118
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8119
0
  if (input.empty()) {
8120
0
    clear_search();
8121
0
    return;
8122
0
  }
8123
8124
0
  if (input[0] == '?') {
8125
0
    input.remove_prefix(1);
8126
0
  }
8127
8128
0
  if (components.hash_start == url_components::omitted) {
8129
0
    if (components.search_start == url_components::omitted) {
8130
0
      components.search_start = uint32_t(buffer.size());
8131
0
      buffer += "?";
8132
0
    } else {
8133
0
      buffer.resize(components.search_start + 1);
8134
0
    }
8135
8136
0
    buffer.append(input);
8137
0
  } else {
8138
0
    if (components.search_start == url_components::omitted) {
8139
0
      components.search_start = components.hash_start;
8140
0
    } else {
8141
0
      buffer.erase(components.search_start,
8142
0
                   components.hash_start - components.search_start);
8143
0
      components.hash_start = components.search_start;
8144
0
    }
8145
8146
0
    buffer.insert(components.search_start, "?");
8147
0
    buffer.insert(components.search_start + 1, input);
8148
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8149
0
  }
8150
8151
0
  ADA_ASSERT_TRUE(validate());
8152
0
}
8153
8154
inline void url_aggregator::update_base_search(
8155
3.98k
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8156
3.98k
  ada_log("url_aggregator::update_base_search ", input,
8157
3.98k
          " with encoding parameter ", to_string(), "\n", to_diagram());
8158
3.98k
  ADA_ASSERT_TRUE(validate());
8159
3.98k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8160
8161
3.98k
  if (components.hash_start == url_components::omitted) {
8162
3.86k
    if (components.search_start == url_components::omitted) {
8163
2.60k
      components.search_start = uint32_t(buffer.size());
8164
2.60k
      buffer += "?";
8165
2.60k
    } else {
8166
1.25k
      buffer.resize(components.search_start + 1);
8167
1.25k
    }
8168
8169
3.86k
    bool encoding_required =
8170
3.86k
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8171
    // When encoding_required is false, then buffer is left unchanged, and
8172
    // percent encoding was not deemed required.
8173
3.86k
    if (!encoding_required) {
8174
2.88k
      buffer.append(input);
8175
2.88k
    }
8176
3.86k
  } else {
8177
116
    if (components.search_start == url_components::omitted) {
8178
0
      components.search_start = components.hash_start;
8179
116
    } else {
8180
116
      buffer.erase(components.search_start,
8181
116
                   components.hash_start - components.search_start);
8182
116
      components.hash_start = components.search_start;
8183
116
    }
8184
8185
116
    buffer.insert(components.search_start, "?");
8186
116
    size_t idx =
8187
116
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8188
116
    if (idx == input.size()) {
8189
116
      buffer.insert(components.search_start + 1, input);
8190
116
      components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8191
116
    } else {
8192
0
      buffer.insert(components.search_start + 1, input, 0, idx);
8193
0
      input.remove_prefix(idx);
8194
      // We only create a temporary string if we need percent encoding and
8195
      // we attempt to create as small a temporary string as we can.
8196
0
      std::string encoded =
8197
0
          ada::unicode::percent_encode(input, query_percent_encode_set);
8198
0
      buffer.insert(components.search_start + idx + 1, encoded);
8199
0
      components.hash_start +=
8200
0
          uint32_t(encoded.size() + idx + 1);  // Do not forget `?`
8201
0
    }
8202
116
  }
8203
8204
3.98k
  ADA_ASSERT_TRUE(validate());
8205
3.98k
}
8206
8207
0
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8208
0
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8209
0
          " bytes] \n", to_diagram());
8210
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8211
0
  ADA_ASSERT_TRUE(validate());
8212
8213
0
  const bool begins_with_dashdash = input.starts_with("//");
8214
0
  if (!begins_with_dashdash && has_dash_dot()) {
8215
    // We must delete the ./
8216
0
    delete_dash_dot();
8217
0
  }
8218
8219
0
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8220
0
      !has_dash_dot()) {
8221
    // If url's host is null, url does not have an opaque path, url's path's
8222
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8223
    // output.
8224
0
    buffer.insert(components.pathname_start, "/.");
8225
0
    components.pathname_start += 2;
8226
0
    if (components.search_start != url_components::omitted) {
8227
0
      components.search_start += 2;
8228
0
    }
8229
0
    if (components.hash_start != url_components::omitted) {
8230
0
      components.hash_start += 2;
8231
0
    }
8232
0
  }
8233
8234
0
  uint32_t difference = replace_and_resize(
8235
0
      components.pathname_start,
8236
0
      components.pathname_start + get_pathname_length(), input);
8237
0
  if (components.search_start != url_components::omitted) {
8238
0
    components.search_start += difference;
8239
0
  }
8240
0
  if (components.hash_start != url_components::omitted) {
8241
0
    components.hash_start += difference;
8242
0
  }
8243
0
  ADA_ASSERT_TRUE(validate());
8244
0
}
8245
8246
0
inline void url_aggregator::append_base_pathname(const std::string_view input) {
8247
0
  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
8248
0
          "\n", to_diagram());
8249
0
  ADA_ASSERT_TRUE(validate());
8250
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8251
#if ADA_DEVELOPMENT_CHECKS
8252
  // computing the expected password.
8253
  std::string path_expected(get_pathname());
8254
  path_expected.append(input);
8255
#endif  // ADA_DEVELOPMENT_CHECKS
8256
0
  uint32_t ending_index = uint32_t(buffer.size());
8257
0
  if (components.search_start != url_components::omitted) {
8258
0
    ending_index = components.search_start;
8259
0
  } else if (components.hash_start != url_components::omitted) {
8260
0
    ending_index = components.hash_start;
8261
0
  }
8262
0
  buffer.insert(ending_index, input);
8263
8264
0
  if (components.search_start != url_components::omitted) {
8265
0
    components.search_start += uint32_t(input.size());
8266
0
  }
8267
0
  if (components.hash_start != url_components::omitted) {
8268
0
    components.hash_start += uint32_t(input.size());
8269
0
  }
8270
#if ADA_DEVELOPMENT_CHECKS
8271
  std::string path_after = std::string(get_pathname());
8272
  ADA_ASSERT_EQUAL(
8273
      path_expected, path_after,
8274
      "append_base_pathname problem after inserting " + std::string(input));
8275
#endif  // ADA_DEVELOPMENT_CHECKS
8276
0
  ADA_ASSERT_TRUE(validate());
8277
0
}
8278
8279
0
inline void url_aggregator::update_base_username(const std::string_view input) {
8280
0
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8281
0
          "\n", to_diagram());
8282
0
  ADA_ASSERT_TRUE(validate());
8283
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8284
8285
0
  add_authority_slashes_if_needed();
8286
8287
0
  bool has_password = has_non_empty_password();
8288
0
  bool host_starts_with_at = buffer.size() > components.host_start &&
8289
0
                             buffer[components.host_start] == '@';
8290
0
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8291
0
                                     components.username_end, input);
8292
8293
0
  components.username_end += diff;
8294
0
  components.host_start += diff;
8295
8296
0
  if (!input.empty() && !host_starts_with_at) {
8297
0
    buffer.insert(components.host_start, "@");
8298
0
    diff++;
8299
0
  } else if (input.empty() && host_starts_with_at && !has_password) {
8300
    // Input is empty, there is no password, and we need to remove "@" from
8301
    // hostname
8302
0
    buffer.erase(components.host_start, 1);
8303
0
    diff--;
8304
0
  }
8305
8306
0
  components.host_end += diff;
8307
0
  components.pathname_start += diff;
8308
0
  if (components.search_start != url_components::omitted) {
8309
0
    components.search_start += diff;
8310
0
  }
8311
0
  if (components.hash_start != url_components::omitted) {
8312
0
    components.hash_start += diff;
8313
0
  }
8314
0
  ADA_ASSERT_TRUE(validate());
8315
0
}
8316
8317
0
inline void url_aggregator::append_base_username(const std::string_view input) {
8318
0
  ada_log("url_aggregator::append_base_username ", input);
8319
0
  ADA_ASSERT_TRUE(validate());
8320
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8321
#if ADA_DEVELOPMENT_CHECKS
8322
  // computing the expected password.
8323
  std::string username_expected(get_username());
8324
  username_expected.append(input);
8325
#endif  // ADA_DEVELOPMENT_CHECKS
8326
0
  add_authority_slashes_if_needed();
8327
8328
  // If input is empty, do nothing.
8329
0
  if (input.empty()) {
8330
0
    return;
8331
0
  }
8332
8333
0
  uint32_t difference = uint32_t(input.size());
8334
0
  buffer.insert(components.username_end, input);
8335
0
  components.username_end += difference;
8336
0
  components.host_start += difference;
8337
8338
0
  if (buffer[components.host_start] != '@' &&
8339
0
      components.host_start != components.host_end) {
8340
0
    buffer.insert(components.host_start, "@");
8341
0
    difference++;
8342
0
  }
8343
8344
0
  components.host_end += difference;
8345
0
  components.pathname_start += difference;
8346
0
  if (components.search_start != url_components::omitted) {
8347
0
    components.search_start += difference;
8348
0
  }
8349
0
  if (components.hash_start != url_components::omitted) {
8350
0
    components.hash_start += difference;
8351
0
  }
8352
#if ADA_DEVELOPMENT_CHECKS
8353
  std::string username_after(get_username());
8354
  ADA_ASSERT_EQUAL(
8355
      username_expected, username_after,
8356
      "append_base_username problem after inserting " + std::string(input));
8357
#endif  // ADA_DEVELOPMENT_CHECKS
8358
0
  ADA_ASSERT_TRUE(validate());
8359
0
}
8360
8361
0
constexpr void url_aggregator::clear_password() {
8362
0
  ada_log("url_aggregator::clear_password ", to_string());
8363
0
  ADA_ASSERT_TRUE(validate());
8364
0
  if (!has_password()) {
8365
0
    return;
8366
0
  }
8367
8368
0
  uint32_t diff = components.host_start - components.username_end;
8369
0
  buffer.erase(components.username_end, diff);
8370
0
  components.host_start -= diff;
8371
0
  components.host_end -= diff;
8372
0
  components.pathname_start -= diff;
8373
0
  if (components.search_start != url_components::omitted) {
8374
0
    components.search_start -= diff;
8375
0
  }
8376
0
  if (components.hash_start != url_components::omitted) {
8377
0
    components.hash_start -= diff;
8378
0
  }
8379
0
}
8380
8381
0
inline void url_aggregator::update_base_password(const std::string_view input) {
8382
0
  ada_log("url_aggregator::update_base_password ", input);
8383
0
  ADA_ASSERT_TRUE(validate());
8384
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8385
8386
0
  add_authority_slashes_if_needed();
8387
8388
  // TODO: Optimization opportunity. Merge the following removal functions.
8389
0
  if (input.empty()) {
8390
0
    clear_password();
8391
8392
    // Remove username too, if it is empty.
8393
0
    if (!has_non_empty_username()) {
8394
0
      update_base_username("");
8395
0
    }
8396
8397
0
    return;
8398
0
  }
8399
8400
0
  bool password_exists = has_password();
8401
0
  uint32_t difference = uint32_t(input.size());
8402
8403
0
  if (password_exists) {
8404
0
    uint32_t current_length =
8405
0
        components.host_start - components.username_end - 1;
8406
0
    buffer.erase(components.username_end + 1, current_length);
8407
0
    difference -= current_length;
8408
0
  } else {
8409
0
    buffer.insert(components.username_end, ":");
8410
0
    difference++;
8411
0
  }
8412
8413
0
  buffer.insert(components.username_end + 1, input);
8414
0
  components.host_start += difference;
8415
8416
  // The following line is required to add "@" to hostname. When updating
8417
  // password if hostname does not start with "@", it is "update_base_password"s
8418
  // responsibility to set it.
8419
0
  if (buffer[components.host_start] != '@') {
8420
0
    buffer.insert(components.host_start, "@");
8421
0
    difference++;
8422
0
  }
8423
8424
0
  components.host_end += difference;
8425
0
  components.pathname_start += difference;
8426
0
  if (components.search_start != url_components::omitted) {
8427
0
    components.search_start += difference;
8428
0
  }
8429
0
  if (components.hash_start != url_components::omitted) {
8430
0
    components.hash_start += difference;
8431
0
  }
8432
0
  ADA_ASSERT_TRUE(validate());
8433
0
}
8434
8435
0
inline void url_aggregator::append_base_password(const std::string_view input) {
8436
0
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8437
0
          "\n", to_diagram());
8438
0
  ADA_ASSERT_TRUE(validate());
8439
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8440
#if ADA_DEVELOPMENT_CHECKS
8441
  // computing the expected password.
8442
  std::string password_expected = std::string(get_password());
8443
  password_expected.append(input);
8444
#endif  // ADA_DEVELOPMENT_CHECKS
8445
0
  add_authority_slashes_if_needed();
8446
8447
  // If input is empty, do nothing.
8448
0
  if (input.empty()) {
8449
0
    return;
8450
0
  }
8451
8452
0
  uint32_t difference = uint32_t(input.size());
8453
0
  if (has_password()) {
8454
0
    buffer.insert(components.host_start, input);
8455
0
  } else {
8456
0
    difference++;  // Increment for ":"
8457
0
    buffer.insert(components.username_end, ":");
8458
0
    buffer.insert(components.username_end + 1, input);
8459
0
  }
8460
0
  components.host_start += difference;
8461
8462
  // The following line is required to add "@" to hostname. When updating
8463
  // password if hostname does not start with "@", it is "append_base_password"s
8464
  // responsibility to set it.
8465
0
  if (buffer[components.host_start] != '@') {
8466
0
    buffer.insert(components.host_start, "@");
8467
0
    difference++;
8468
0
  }
8469
8470
0
  components.host_end += difference;
8471
0
  components.pathname_start += difference;
8472
0
  if (components.search_start != url_components::omitted) {
8473
0
    components.search_start += difference;
8474
0
  }
8475
0
  if (components.hash_start != url_components::omitted) {
8476
0
    components.hash_start += difference;
8477
0
  }
8478
#if ADA_DEVELOPMENT_CHECKS
8479
  std::string password_after(get_password());
8480
  ADA_ASSERT_EQUAL(
8481
      password_expected, password_after,
8482
      "append_base_password problem after inserting " + std::string(input));
8483
#endif  // ADA_DEVELOPMENT_CHECKS
8484
0
  ADA_ASSERT_TRUE(validate());
8485
0
}
8486
8487
0
inline void url_aggregator::update_base_port(uint32_t input) {
8488
0
  ada_log("url_aggregator::update_base_port");
8489
0
  ADA_ASSERT_TRUE(validate());
8490
0
  if (input == url_components::omitted) {
8491
0
    clear_port();
8492
0
    return;
8493
0
  }
8494
  // calling std::to_string(input.value()) is unfortunate given that the port
8495
  // value is probably already available as a string.
8496
0
  std::string value = helpers::concat(":", std::to_string(input));
8497
0
  uint32_t difference = uint32_t(value.size());
8498
8499
0
  if (components.port != url_components::omitted) {
8500
0
    difference -= components.pathname_start - components.host_end;
8501
0
    buffer.erase(components.host_end,
8502
0
                 components.pathname_start - components.host_end);
8503
0
  }
8504
8505
0
  buffer.insert(components.host_end, value);
8506
0
  components.pathname_start += difference;
8507
0
  if (components.search_start != url_components::omitted) {
8508
0
    components.search_start += difference;
8509
0
  }
8510
0
  if (components.hash_start != url_components::omitted) {
8511
0
    components.hash_start += difference;
8512
0
  }
8513
0
  components.port = input;
8514
0
  ADA_ASSERT_TRUE(validate());
8515
0
}
8516
8517
0
inline void url_aggregator::clear_port() {
8518
0
  ada_log("url_aggregator::clear_port");
8519
0
  ADA_ASSERT_TRUE(validate());
8520
0
  if (components.port == url_components::omitted) {
8521
0
    return;
8522
0
  }
8523
0
  uint32_t length = components.pathname_start - components.host_end;
8524
0
  buffer.erase(components.host_end, length);
8525
0
  components.pathname_start -= length;
8526
0
  if (components.search_start != url_components::omitted) {
8527
0
    components.search_start -= length;
8528
0
  }
8529
0
  if (components.hash_start != url_components::omitted) {
8530
0
    components.hash_start -= length;
8531
0
  }
8532
0
  components.port = url_components::omitted;
8533
0
  ADA_ASSERT_TRUE(validate());
8534
0
}
8535
8536
0
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8537
0
  ada_log("url_aggregator::retrieve_base_port");
8538
0
  return components.port;
8539
0
}
8540
8541
1.23k
inline void url_aggregator::clear_search() {
8542
1.23k
  ada_log("url_aggregator::clear_search");
8543
1.23k
  ADA_ASSERT_TRUE(validate());
8544
1.23k
  if (components.search_start == url_components::omitted) {
8545
0
    return;
8546
0
  }
8547
8548
1.23k
  if (components.hash_start == url_components::omitted) {
8549
1.18k
    buffer.resize(components.search_start);
8550
1.18k
  } else {
8551
44
    buffer.erase(components.search_start,
8552
44
                 components.hash_start - components.search_start);
8553
44
    components.hash_start = components.search_start;
8554
44
  }
8555
8556
1.23k
  components.search_start = url_components::omitted;
8557
8558
#if ADA_DEVELOPMENT_CHECKS
8559
  ADA_ASSERT_EQUAL(get_search(), "",
8560
                   "search should have been cleared on buffer=" + buffer +
8561
                       " with " + components.to_string() + "\n" + to_diagram());
8562
#endif
8563
1.23k
  ADA_ASSERT_TRUE(validate());
8564
1.23k
}
8565
8566
0
inline void url_aggregator::clear_hash() {
8567
0
  ada_log("url_aggregator::clear_hash");
8568
0
  ADA_ASSERT_TRUE(validate());
8569
0
  if (components.hash_start == url_components::omitted) {
8570
0
    return;
8571
0
  }
8572
0
  buffer.resize(components.hash_start);
8573
0
  components.hash_start = url_components::omitted;
8574
8575
#if ADA_DEVELOPMENT_CHECKS
8576
  ADA_ASSERT_EQUAL(get_hash(), "",
8577
                   "hash should have been cleared on buffer=" + buffer +
8578
                       " with " + components.to_string() + "\n" + to_diagram());
8579
#endif
8580
0
  ADA_ASSERT_TRUE(validate());
8581
0
}
8582
8583
0
constexpr void url_aggregator::clear_pathname() {
8584
0
  ada_log("url_aggregator::clear_pathname");
8585
0
  ADA_ASSERT_TRUE(validate());
8586
0
  uint32_t ending_index = uint32_t(buffer.size());
8587
0
  if (components.search_start != url_components::omitted) {
8588
0
    ending_index = components.search_start;
8589
0
  } else if (components.hash_start != url_components::omitted) {
8590
0
    ending_index = components.hash_start;
8591
0
  }
8592
0
  uint32_t pathname_length = ending_index - components.pathname_start;
8593
0
  buffer.erase(components.pathname_start, pathname_length);
8594
0
  uint32_t difference = pathname_length;
8595
0
  if (components.pathname_start == components.host_end + 2 &&
8596
0
      buffer[components.host_end] == '/' &&
8597
0
      buffer[components.host_end + 1] == '.') {
8598
0
    components.pathname_start -= 2;
8599
0
    buffer.erase(components.host_end, 2);
8600
0
    difference += 2;
8601
0
  }
8602
0
  if (components.search_start != url_components::omitted) {
8603
0
    components.search_start -= difference;
8604
0
  }
8605
0
  if (components.hash_start != url_components::omitted) {
8606
0
    components.hash_start -= difference;
8607
0
  }
8608
0
  ada_log("url_aggregator::clear_pathname completed, running checks...");
8609
#if ADA_DEVELOPMENT_CHECKS
8610
  ADA_ASSERT_EQUAL(get_pathname(), "",
8611
                   "pathname should have been cleared on buffer=" + buffer +
8612
                       " with " + components.to_string() + "\n" + to_diagram());
8613
#endif
8614
0
  ADA_ASSERT_TRUE(validate());
8615
0
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
8616
0
}
8617
8618
0
constexpr void url_aggregator::clear_hostname() {
8619
0
  ada_log("url_aggregator::clear_hostname");
8620
0
  ADA_ASSERT_TRUE(validate());
8621
0
  if (!has_authority()) {
8622
0
    return;
8623
0
  }
8624
0
  ADA_ASSERT_TRUE(has_authority());
8625
8626
0
  uint32_t hostname_length = components.host_end - components.host_start;
8627
0
  uint32_t start = components.host_start;
8628
8629
  // If hostname starts with "@", we should not remove that character.
8630
0
  if (hostname_length > 0 && buffer[start] == '@') {
8631
0
    start++;
8632
0
    hostname_length--;
8633
0
  }
8634
0
  buffer.erase(start, hostname_length);
8635
0
  components.host_end = start;
8636
0
  components.pathname_start -= hostname_length;
8637
0
  if (components.search_start != url_components::omitted) {
8638
0
    components.search_start -= hostname_length;
8639
0
  }
8640
0
  if (components.hash_start != url_components::omitted) {
8641
0
    components.hash_start -= hostname_length;
8642
0
  }
8643
#if ADA_DEVELOPMENT_CHECKS
8644
  ADA_ASSERT_EQUAL(get_hostname(), "",
8645
                   "hostname should have been cleared on buffer=" + buffer +
8646
                       " with " + components.to_string() + "\n" + to_diagram());
8647
#endif
8648
0
  ADA_ASSERT_TRUE(has_authority());
8649
0
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
8650
0
                   "hostname should have been cleared on buffer=" + buffer +
8651
0
                       " with " + components.to_string() + "\n" + to_diagram());
8652
0
  ADA_ASSERT_TRUE(validate());
8653
0
}
8654
8655
0
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
8656
0
  ada_log("url_aggregator::has_hash");
8657
0
  return components.hash_start != url_components::omitted;
8658
0
}
8659
8660
0
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
8661
0
  ada_log("url_aggregator::has_search");
8662
0
  return components.search_start != url_components::omitted;
8663
0
}
8664
8665
0
constexpr bool url_aggregator::has_credentials() const noexcept {
8666
0
  ada_log("url_aggregator::has_credentials");
8667
0
  return has_non_empty_username() || has_non_empty_password();
8668
0
}
8669
8670
0
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
8671
0
  ada_log("url_aggregator::cannot_have_credentials_or_port");
8672
0
  return type == ada::scheme::type::FILE ||
8673
0
         components.host_start == components.host_end;
8674
0
}
8675
8676
[[nodiscard]] ada_really_inline const ada::url_components&
8677
0
url_aggregator::get_components() const noexcept {
8678
0
  return components;
8679
0
}
8680
8681
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
8682
2.60k
    const noexcept {
8683
2.60k
  ada_log("url_aggregator::has_authority");
8684
  // Performance: instead of doing this potentially expensive check, we could
8685
  // have a boolean in the struct.
8686
2.60k
  return components.protocol_end + 2 <= components.host_start &&
8687
0
         buffer[components.protocol_end] == '/' &&
8688
0
         buffer[components.protocol_end + 1] == '/';
8689
2.60k
}
8690
8691
2.60k
inline void ada::url_aggregator::add_authority_slashes_if_needed() {
8692
2.60k
  ada_log("url_aggregator::add_authority_slashes_if_needed");
8693
2.60k
  ADA_ASSERT_TRUE(validate());
8694
  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
8695
  // to insert
8696
  // `//` initially to the buffer, since it depends on the hostname existence.
8697
2.60k
  if (has_authority()) {
8698
0
    return;
8699
0
  }
8700
  // Performance: the common case is components.protocol_end == buffer.size()
8701
  // Optimization opportunity: in many cases, the "//" is part of the input and
8702
  // the insert could be fused with another insert.
8703
2.60k
  buffer.insert(components.protocol_end, "//");
8704
2.60k
  components.username_end += 2;
8705
2.60k
  components.host_start += 2;
8706
2.60k
  components.host_end += 2;
8707
2.60k
  components.pathname_start += 2;
8708
2.60k
  if (components.search_start != url_components::omitted) {
8709
0
    components.search_start += 2;
8710
0
  }
8711
2.60k
  if (components.hash_start != url_components::omitted) {
8712
0
    components.hash_start += 2;
8713
0
  }
8714
2.60k
  ADA_ASSERT_TRUE(validate());
8715
2.60k
}
8716
8717
2.60k
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
8718
2.60k
  buffer.reserve(capacity);
8719
2.60k
}
8720
8721
0
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
8722
0
  ada_log("url_aggregator::has_non_empty_username");
8723
0
  return components.protocol_end + 2 < components.username_end;
8724
0
}
8725
8726
0
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
8727
0
  ada_log("url_aggregator::has_non_empty_password");
8728
0
  return components.host_start > components.username_end;
8729
0
}
8730
8731
0
constexpr bool url_aggregator::has_password() const noexcept {
8732
0
  ada_log("url_aggregator::has_password");
8733
  // This function does not care about the length of the password
8734
0
  return components.host_start > components.username_end &&
8735
0
         buffer[components.username_end] == ':';
8736
0
}
8737
8738
0
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
8739
0
  if (!has_hostname()) {
8740
0
    return false;
8741
0
  }
8742
0
  if (components.host_start == components.host_end) {
8743
0
    return true;
8744
0
  }
8745
0
  if (components.host_end > components.host_start + 1) {
8746
0
    return false;
8747
0
  }
8748
0
  return components.username_end != components.host_start;
8749
0
}
8750
8751
0
constexpr bool url_aggregator::has_hostname() const noexcept {
8752
0
  return has_authority();
8753
0
}
8754
8755
0
constexpr bool url_aggregator::has_port() const noexcept {
8756
0
  ada_log("url_aggregator::has_port");
8757
  // A URL cannot have a username/password/port if its host is null or the empty
8758
  // string, or its scheme is "file".
8759
0
  return has_hostname() && components.pathname_start != components.host_end;
8760
0
}
8761
8762
0
[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
8763
  // If url's host is null, url does not have an opaque path, url's path's size
8764
  // is greater than 1, and url's path[0] is the empty string, then append
8765
  // U+002F (/) followed by U+002E (.) to output.
8766
0
  ada_log("url_aggregator::has_dash_dot");
8767
#if ADA_DEVELOPMENT_CHECKS
8768
  // If pathname_start and host_end are exactly two characters apart, then we
8769
  // either have a one-digit port such as http://test.com:5?param=1 or else we
8770
  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
8771
  if (components.pathname_start == components.host_end + 2) {
8772
    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
8773
                     buffer[components.host_end + 1] == '.') ||
8774
                    (buffer[components.host_end] == ':' &&
8775
                     checkers::is_digit(buffer[components.host_end + 1])));
8776
  }
8777
  if (components.pathname_start == components.host_end + 2 &&
8778
      buffer[components.host_end] == '/' &&
8779
      buffer[components.host_end + 1] == '.') {
8780
    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
8781
    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
8782
    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
8783
  }
8784
#endif
8785
  // Performance: it should be uncommon for components.pathname_start ==
8786
  // components.host_end + 2 to be true. So we put this check first in the
8787
  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
8788
  // more expensive, but should be uncommon.
8789
0
  return components.pathname_start == components.host_end + 2 &&
8790
0
         !has_opaque_path && buffer[components.host_end] == '/' &&
8791
0
         buffer[components.host_end + 1] == '.';
8792
0
}
8793
8794
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
8795
0
    const noexcept ada_lifetime_bound {
8796
0
  ada_log("url_aggregator::get_href");
8797
0
  return buffer;
8798
0
}
8799
8800
0
[[nodiscard]] constexpr size_t url_aggregator::get_href_size() const noexcept {
8801
0
  return buffer.size();
8802
0
}
8803
8804
ada_really_inline size_t
8805
0
url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
8806
0
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
8807
0
  if (!view.empty() && view[0] == '-') {
8808
0
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
8809
0
    is_valid = false;
8810
0
    return 0;
8811
0
  }
8812
0
  uint16_t parsed_port{};
8813
0
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
8814
0
  if (r.ec == std::errc::result_out_of_range) {
8815
0
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
8816
0
    is_valid = false;
8817
0
    return 0;
8818
0
  }
8819
0
  ada_log("parse_port: ", parsed_port);
8820
0
  const size_t consumed = size_t(r.ptr - view.data());
8821
0
  ada_log("parse_port: consumed ", consumed);
8822
0
  if (check_trailing_content) {
8823
0
    is_valid &=
8824
0
        (consumed == view.size() || view[consumed] == '/' ||
8825
0
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
8826
0
  }
8827
0
  ada_log("parse_port: is_valid = ", is_valid);
8828
0
  if (is_valid) {
8829
0
    ada_log("parse_port", r.ec == std::errc());
8830
    // scheme_default_port can return 0, and we should allow 0 as a base port.
8831
0
    auto default_port = scheme_default_port();
8832
0
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
8833
0
                         (default_port != parsed_port);
8834
0
    if (r.ec == std::errc() && is_port_valid) {
8835
0
      update_base_port(parsed_port);
8836
0
    } else {
8837
0
      clear_port();
8838
0
    }
8839
0
  }
8840
0
  return consumed;
8841
0
}
8842
8843
0
constexpr void url_aggregator::set_protocol_as_file() {
8844
0
  ada_log("url_aggregator::set_protocol_as_file ");
8845
0
  ADA_ASSERT_TRUE(validate());
8846
0
  type = ada::scheme::type::FILE;
8847
  // next line could overflow but unsigned arithmetic has well-defined
8848
  // overflows.
8849
0
  uint32_t new_difference = 5 - components.protocol_end;
8850
8851
0
  if (buffer.empty()) {
8852
0
    buffer.append("file:");
8853
0
  } else {
8854
0
    buffer.erase(0, components.protocol_end);
8855
0
    buffer.insert(0, "file:");
8856
0
  }
8857
0
  components.protocol_end = 5;
8858
8859
  // Update the rest of the components.
8860
0
  components.username_end += new_difference;
8861
0
  components.host_start += new_difference;
8862
0
  components.host_end += new_difference;
8863
0
  components.pathname_start += new_difference;
8864
0
  if (components.search_start != url_components::omitted) {
8865
0
    components.search_start += new_difference;
8866
0
  }
8867
0
  if (components.hash_start != url_components::omitted) {
8868
0
    components.hash_start += new_difference;
8869
0
  }
8870
0
  ADA_ASSERT_TRUE(validate());
8871
0
}
8872
8873
2.60k
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
8874
2.60k
  if (!is_valid) {
8875
0
    return true;
8876
0
  }
8877
2.60k
  if (!components.check_offset_consistency()) {
8878
0
    ada_log("url_aggregator::validate inconsistent components \n",
8879
0
            to_diagram());
8880
0
    return false;
8881
0
  }
8882
  // We have a credible components struct, but let us investivate more
8883
  // carefully:
8884
  /**
8885
   * https://user:pass@example.com:1234/foo/bar?baz#quux
8886
   *       |     |    |          | ^^^^|       |   |
8887
   *       |     |    |          | |   |       |   `----- hash_start
8888
   *       |     |    |          | |   |       `--------- search_start
8889
   *       |     |    |          | |   `----------------- pathname_start
8890
   *       |     |    |          | `--------------------- port
8891
   *       |     |    |          `----------------------- host_end
8892
   *       |     |    `---------------------------------- host_start
8893
   *       |     `--------------------------------------- username_end
8894
   *       `--------------------------------------------- protocol_end
8895
   */
8896
2.60k
  if (components.protocol_end == url_components::omitted) {
8897
0
    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
8898
0
    return false;
8899
0
  }
8900
2.60k
  if (components.username_end == url_components::omitted) {
8901
0
    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
8902
0
    return false;
8903
0
  }
8904
2.60k
  if (components.host_start == url_components::omitted) {
8905
0
    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
8906
0
    return false;
8907
0
  }
8908
2.60k
  if (components.host_end == url_components::omitted) {
8909
0
    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
8910
0
    return false;
8911
0
  }
8912
2.60k
  if (components.pathname_start == url_components::omitted) {
8913
0
    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
8914
0
    return false;
8915
0
  }
8916
8917
2.60k
  if (components.protocol_end > buffer.size()) {
8918
0
    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
8919
0
    return false;
8920
0
  }
8921
2.60k
  if (components.username_end > buffer.size()) {
8922
0
    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
8923
0
    return false;
8924
0
  }
8925
2.60k
  if (components.host_start > buffer.size()) {
8926
0
    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
8927
0
    return false;
8928
0
  }
8929
2.60k
  if (components.host_end > buffer.size()) {
8930
0
    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
8931
0
    return false;
8932
0
  }
8933
2.60k
  if (components.pathname_start > buffer.size()) {
8934
0
    ada_log("url_aggregator::validate pathname_start overflow \n",
8935
0
            to_diagram());
8936
0
    return false;
8937
0
  }
8938
8939
2.60k
  if (components.protocol_end > 0) {
8940
2.60k
    if (buffer[components.protocol_end - 1] != ':') {
8941
0
      ada_log(
8942
0
          "url_aggregator::validate missing : at the end of the protocol \n",
8943
0
          to_diagram());
8944
0
      return false;
8945
0
    }
8946
2.60k
  }
8947
8948
2.60k
  if (components.username_end != buffer.size() &&
8949
2.60k
      components.username_end > components.protocol_end + 2) {
8950
0
    if (buffer[components.username_end] != ':' &&
8951
0
        buffer[components.username_end] != '@') {
8952
0
      ada_log(
8953
0
          "url_aggregator::validate missing : or @ at the end of the username "
8954
0
          "\n",
8955
0
          to_diagram());
8956
0
      return false;
8957
0
    }
8958
0
  }
8959
8960
2.60k
  if (components.host_start != buffer.size()) {
8961
2.60k
    if (components.host_start > components.username_end) {
8962
0
      if (buffer[components.host_start] != '@') {
8963
0
        ada_log(
8964
0
            "url_aggregator::validate missing @ at the end of the password \n",
8965
0
            to_diagram());
8966
0
        return false;
8967
0
      }
8968
2.60k
    } else if (components.host_start == components.username_end &&
8969
2.60k
               components.host_end > components.host_start) {
8970
2.60k
      if (components.host_start == components.protocol_end + 2) {
8971
2.60k
        if (buffer[components.protocol_end] != '/' ||
8972
2.60k
            buffer[components.protocol_end + 1] != '/') {
8973
0
          ada_log(
8974
0
              "url_aggregator::validate missing // between protocol and host "
8975
0
              "\n",
8976
0
              to_diagram());
8977
0
          return false;
8978
0
        }
8979
2.60k
      } else {
8980
0
        if (components.host_start > components.protocol_end &&
8981
0
            buffer[components.host_start] != '@') {
8982
0
          ada_log(
8983
0
              "url_aggregator::validate missing @ at the end of the username "
8984
0
              "\n",
8985
0
              to_diagram());
8986
0
          return false;
8987
0
        }
8988
0
      }
8989
2.60k
    } else {
8990
0
      if (components.host_end != components.host_start) {
8991
0
        ada_log("url_aggregator::validate expected omitted host \n",
8992
0
                to_diagram());
8993
0
        return false;
8994
0
      }
8995
0
    }
8996
2.60k
  }
8997
2.60k
  if (components.host_end != buffer.size() &&
8998
2.60k
      components.pathname_start > components.host_end) {
8999
0
    if (components.pathname_start == components.host_end + 2 &&
9000
0
        buffer[components.host_end] == '/' &&
9001
0
        buffer[components.host_end + 1] == '.') {
9002
0
      if (components.pathname_start + 1 >= buffer.size() ||
9003
0
          buffer[components.pathname_start] != '/' ||
9004
0
          buffer[components.pathname_start + 1] != '/') {
9005
0
        ada_log(
9006
0
            "url_aggregator::validate expected the path to begin with // \n",
9007
0
            to_diagram());
9008
0
        return false;
9009
0
      }
9010
0
    } else if (buffer[components.host_end] != ':') {
9011
0
      ada_log("url_aggregator::validate missing : at the port \n",
9012
0
              to_diagram());
9013
0
      return false;
9014
0
    }
9015
0
  }
9016
2.60k
  if (components.pathname_start != buffer.size() &&
9017
2.60k
      components.pathname_start < components.search_start &&
9018
2.60k
      components.pathname_start < components.hash_start && !has_opaque_path) {
9019
2.60k
    if (buffer[components.pathname_start] != '/') {
9020
0
      ada_log("url_aggregator::validate missing / at the path \n",
9021
0
              to_diagram());
9022
0
      return false;
9023
0
    }
9024
2.60k
  }
9025
2.60k
  if (components.search_start != url_components::omitted) {
9026
1.37k
    if (buffer[components.search_start] != '?') {
9027
0
      ada_log("url_aggregator::validate missing ? at the search \n",
9028
0
              to_diagram());
9029
0
      return false;
9030
0
    }
9031
1.37k
  }
9032
2.60k
  if (components.hash_start != url_components::omitted) {
9033
160
    if (buffer[components.hash_start] != '#') {
9034
0
      ada_log("url_aggregator::validate missing # at the hash \n",
9035
0
              to_diagram());
9036
0
      return false;
9037
0
    }
9038
160
  }
9039
9040
2.60k
  return true;
9041
2.60k
}
9042
9043
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() const
9044
0
    ada_lifetime_bound {
9045
0
  ada_log("url_aggregator::get_pathname pathname_start = ",
9046
0
          components.pathname_start, " buffer.size() = ", buffer.size(),
9047
0
          " components.search_start = ", components.search_start,
9048
0
          " components.hash_start = ", components.hash_start);
9049
0
  auto ending_index = uint32_t(buffer.size());
9050
0
  if (components.search_start != url_components::omitted) {
9051
0
    ending_index = components.search_start;
9052
0
  } else if (components.hash_start != url_components::omitted) {
9053
0
    ending_index = components.hash_start;
9054
0
  }
9055
0
  return helpers::substring(buffer, components.pathname_start, ending_index);
9056
0
}
9057
9058
inline std::ostream& operator<<(std::ostream& out,
9059
0
                                const ada::url_aggregator& u) {
9060
0
  return out << u.to_string();
9061
0
}
9062
9063
0
void url_aggregator::update_host_to_base_host(const std::string_view input) {
9064
0
  ada_log("url_aggregator::update_host_to_base_host ", input);
9065
0
  ADA_ASSERT_TRUE(validate());
9066
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
9067
0
  if (type != ada::scheme::type::FILE) {
9068
    // Let host be the result of host parsing host_view with url is not special.
9069
0
    if (input.empty() && !is_special()) {
9070
0
      if (has_hostname()) {
9071
0
        clear_hostname();
9072
0
      } else if (has_dash_dot()) {
9073
0
        add_authority_slashes_if_needed();
9074
0
        delete_dash_dot();
9075
0
      }
9076
0
      return;
9077
0
    }
9078
0
  }
9079
0
  update_base_hostname(input);
9080
0
  ADA_ASSERT_TRUE(validate());
9081
0
  return;
9082
0
}
9083
}  // namespace ada
9084
9085
#endif  // ADA_URL_AGGREGATOR_INL_H
9086
/* end file include/ada/url_aggregator-inl.h */
9087
/* begin file include/ada/url_search_params.h */
9088
/**
9089
 * @file url_search_params.h
9090
 * @brief URL query string parameter manipulation.
9091
 *
9092
 * This file provides the `url_search_params` class for parsing, manipulating,
9093
 * and serializing URL query strings. It implements the URLSearchParams API
9094
 * from the WHATWG URL Standard.
9095
 *
9096
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9097
 */
9098
#ifndef ADA_URL_SEARCH_PARAMS_H
9099
#define ADA_URL_SEARCH_PARAMS_H
9100
9101
#include <optional>
9102
#include <string>
9103
#include <string_view>
9104
#include <vector>
9105
9106
namespace ada {
9107
9108
/**
9109
 * @brief Iterator types for url_search_params iteration.
9110
 */
9111
enum class url_search_params_iter_type {
9112
  KEYS,    /**< Iterate over parameter keys only */
9113
  VALUES,  /**< Iterate over parameter values only */
9114
  ENTRIES, /**< Iterate over key-value pairs */
9115
};
9116
9117
template <typename T, url_search_params_iter_type Type>
9118
struct url_search_params_iter;
9119
9120
/** Type alias for a key-value pair of string views. */
9121
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
9122
9123
/** Iterator over search parameter keys. */
9124
using url_search_params_keys_iter =
9125
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
9126
/** Iterator over search parameter values. */
9127
using url_search_params_values_iter =
9128
    url_search_params_iter<std::string_view,
9129
                           url_search_params_iter_type::VALUES>;
9130
/** Iterator over search parameter key-value pairs. */
9131
using url_search_params_entries_iter =
9132
    url_search_params_iter<key_value_view_pair,
9133
                           url_search_params_iter_type::ENTRIES>;
9134
9135
/**
9136
 * @brief Class for parsing and manipulating URL query strings.
9137
 *
9138
 * The `url_search_params` class provides methods to parse, modify, and
9139
 * serialize URL query parameters (the part after '?' in a URL). It handles
9140
 * percent-encoding and decoding automatically.
9141
 *
9142
 * All string inputs must be valid UTF-8. The caller is responsible for
9143
 * ensuring UTF-8 validity.
9144
 *
9145
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9146
 */
9147
struct url_search_params {
9148
7.82k
  url_search_params() = default;
9149
9150
  /**
9151
   * Constructs url_search_params by parsing a query string.
9152
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9153
   */
9154
13.0k
  explicit url_search_params(const std::string_view input) {
9155
13.0k
    initialize(input);
9156
13.0k
  }
9157
9158
2.60k
  url_search_params(const url_search_params& u) = default;
9159
2.60k
  url_search_params(url_search_params&& u) noexcept = default;
9160
2.60k
  url_search_params& operator=(url_search_params&& u) noexcept = default;
9161
2.60k
  url_search_params& operator=(const url_search_params& u) = default;
9162
26.0k
  ~url_search_params() = default;
9163
9164
  /**
9165
   * Returns the number of key-value pairs.
9166
   * @return The total count of parameters.
9167
   */
9168
  [[nodiscard]] inline size_t size() const noexcept;
9169
9170
  /**
9171
   * Appends a new key-value pair to the parameter list.
9172
   * @param key The parameter name (must be valid UTF-8).
9173
   * @param value The parameter value (must be valid UTF-8).
9174
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9175
   */
9176
  inline void append(std::string_view key, std::string_view value);
9177
9178
  /**
9179
   * Removes all pairs with the given key.
9180
   * @param key The parameter name to remove.
9181
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9182
   */
9183
  inline void remove(std::string_view key);
9184
9185
  /**
9186
   * Removes all pairs with the given key and value.
9187
   * @param key The parameter name.
9188
   * @param value The parameter value to match.
9189
   */
9190
  inline void remove(std::string_view key, std::string_view value);
9191
9192
  /**
9193
   * Returns the value of the first pair with the given key.
9194
   * @param key The parameter name to search for.
9195
   * @return The value if found, or std::nullopt if not present.
9196
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9197
   */
9198
  inline std::optional<std::string_view> get(std::string_view key);
9199
9200
  /**
9201
   * Returns all values for pairs with the given key.
9202
   * @param key The parameter name to search for.
9203
   * @return A vector of all matching values (may be empty).
9204
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9205
   */
9206
  inline std::vector<std::string> get_all(std::string_view key);
9207
9208
  /**
9209
   * Checks if any pair has the given key.
9210
   * @param key The parameter name to search for.
9211
   * @return `true` if at least one pair has this key.
9212
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9213
   */
9214
  inline bool has(std::string_view key) noexcept;
9215
9216
  /**
9217
   * Checks if any pair matches the given key and value.
9218
   * @param key The parameter name to search for.
9219
   * @param value The parameter value to match.
9220
   * @return `true` if a matching pair exists.
9221
   */
9222
  inline bool has(std::string_view key, std::string_view value) noexcept;
9223
9224
  /**
9225
   * Sets a parameter value, replacing any existing pairs with the same key.
9226
   * @param key The parameter name (must be valid UTF-8).
9227
   * @param value The parameter value (must be valid UTF-8).
9228
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9229
   */
9230
  inline void set(std::string_view key, std::string_view value);
9231
9232
  /**
9233
   * Sorts all key-value pairs by their keys using code unit comparison.
9234
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9235
   */
9236
  inline void sort();
9237
9238
  /**
9239
   * Serializes the parameters to a query string (without leading '?').
9240
   * @return The percent-encoded query string.
9241
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9242
   */
9243
  inline std::string to_string() const;
9244
9245
  /**
9246
   * Returns an iterator over all parameter keys.
9247
   * Keys may repeat if there are duplicate parameters.
9248
   * @return An iterator yielding string_view keys.
9249
   * @note The iterator is invalidated if this object is modified.
9250
   */
9251
  inline url_search_params_keys_iter get_keys();
9252
9253
  /**
9254
   * Returns an iterator over all parameter values.
9255
   * @return An iterator yielding string_view values.
9256
   * @note The iterator is invalidated if this object is modified.
9257
   */
9258
  inline url_search_params_values_iter get_values();
9259
9260
  /**
9261
   * Returns an iterator over all key-value pairs.
9262
   * @return An iterator yielding key-value pair views.
9263
   * @note The iterator is invalidated if this object is modified.
9264
   */
9265
  inline url_search_params_entries_iter get_entries();
9266
9267
  /**
9268
   * C++ style conventional iterator support. const only because we
9269
   * do not really want the params to be modified via the iterator.
9270
   */
9271
10.4k
  inline auto begin() const { return params.begin(); }
9272
10.4k
  inline auto end() const { return params.end(); }
9273
1.11k
  inline auto front() const { return params.front(); }
9274
1.11k
  inline auto back() const { return params.back(); }
9275
1.11k
  inline auto operator[](size_t index) const { return params[index]; }
9276
9277
  /**
9278
   * @private
9279
   * Used to reset the search params to a new input.
9280
   * Used primarily for C API.
9281
   * @param input
9282
   */
9283
  void reset(std::string_view input);
9284
9285
 private:
9286
  typedef std::pair<std::string, std::string> key_value_pair;
9287
  std::vector<key_value_pair> params{};
9288
9289
  /**
9290
   * The init parameter must be valid UTF-8.
9291
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9292
   */
9293
  void initialize(std::string_view init);
9294
9295
  template <typename T, url_search_params_iter_type Type>
9296
  friend struct url_search_params_iter;
9297
};  // url_search_params
9298
9299
/**
9300
 * @brief JavaScript-style iterator for url_search_params.
9301
 *
9302
 * Provides a `next()` method that returns successive values until exhausted.
9303
 * This matches the iterator pattern used in the Web Platform.
9304
 *
9305
 * @tparam T The type of value returned by the iterator.
9306
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9307
 *
9308
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9309
 */
9310
template <typename T, url_search_params_iter_type Type>
9311
struct url_search_params_iter {
9312
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()
9313
  url_search_params_iter(const url_search_params_iter& u) = default;
9314
  url_search_params_iter(url_search_params_iter&& u) noexcept = default;
9315
  url_search_params_iter& operator=(url_search_params_iter&& u) noexcept =
9316
      default;
9317
  url_search_params_iter& operator=(const url_search_params_iter& u) = default;
9318
  ~url_search_params_iter() = default;
9319
9320
  /**
9321
   * Returns the next value in the iteration sequence.
9322
   * @return The next value, or std::nullopt if iteration is complete.
9323
   */
9324
  inline std::optional<T> next();
9325
9326
  /**
9327
   * Checks if more values are available.
9328
   * @return `true` if `next()` will return a value, `false` if exhausted.
9329
   */
9330
  inline bool has_next() const;
9331
9332
 private:
9333
  static url_search_params EMPTY;
9334
7.82k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::url_search_params_iter(ada::url_search_params&)
Line
Count
Source
9334
2.60k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::url_search_params_iter(ada::url_search_params&)
Line
Count
Source
9334
2.60k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::url_search_params_iter(ada::url_search_params&)
Line
Count
Source
9334
2.60k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
9335
9336
  url_search_params& params;
9337
  size_t pos = 0;
9338
9339
  friend struct url_search_params;
9340
};
9341
9342
}  // namespace ada
9343
#endif
9344
/* end file include/ada/url_search_params.h */
9345
/* begin file include/ada/url_search_params-inl.h */
9346
/**
9347
 * @file url_search_params-inl.h
9348
 * @brief Inline declarations for the URL Search Params
9349
 */
9350
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9351
#define ADA_URL_SEARCH_PARAMS_INL_H
9352
9353
9354
#include <algorithm>
9355
#include <optional>
9356
#include <ranges>
9357
#include <string>
9358
#include <string_view>
9359
#include <vector>
9360
9361
namespace ada {
9362
9363
// A default, empty url_search_params for use with empty iterators.
9364
template <typename T, ada::url_search_params_iter_type Type>
9365
url_search_params url_search_params_iter<T, Type>::EMPTY;
9366
9367
2.60k
inline void url_search_params::reset(std::string_view input) {
9368
2.60k
  params.clear();
9369
2.60k
  initialize(input);
9370
2.60k
}
9371
9372
15.6k
inline void url_search_params::initialize(std::string_view input) {
9373
15.6k
  if (!input.empty() && input.front() == '?') {
9374
24
    input.remove_prefix(1);
9375
24
  }
9376
9377
33.1k
  auto process_key_value = [&](const std::string_view current) {
9378
33.1k
    auto equal = current.find('=');
9379
9380
33.1k
    if (equal == std::string_view::npos) {
9381
11.7k
      std::string name(current);
9382
11.7k
      std::ranges::replace(name, '+', ' ');
9383
11.7k
      params.emplace_back(unicode::percent_decode(name, name.find('%')), "");
9384
21.4k
    } else {
9385
21.4k
      std::string name(current.substr(0, equal));
9386
21.4k
      std::string value(current.substr(equal + 1));
9387
9388
21.4k
      std::ranges::replace(name, '+', ' ');
9389
21.4k
      std::ranges::replace(value, '+', ' ');
9390
9391
21.4k
      params.emplace_back(unicode::percent_decode(name, name.find('%')),
9392
21.4k
                          unicode::percent_decode(value, value.find('%')));
9393
21.4k
    }
9394
33.1k
  };
9395
9396
46.1k
  while (!input.empty()) {
9397
36.0k
    auto ampersand_index = input.find('&');
9398
9399
36.0k
    if (ampersand_index == std::string_view::npos) {
9400
5.59k
      if (!input.empty()) {
9401
5.59k
        process_key_value(input);
9402
5.59k
      }
9403
5.59k
      break;
9404
30.4k
    } else if (ampersand_index != 0) {
9405
27.5k
      process_key_value(input.substr(0, ampersand_index));
9406
27.5k
    }
9407
9408
30.4k
    input.remove_prefix(ampersand_index + 1);
9409
30.4k
  }
9410
15.6k
}
9411
9412
inline void url_search_params::append(const std::string_view key,
9413
14.4k
                                      const std::string_view value) {
9414
14.4k
  params.emplace_back(key, value);
9415
14.4k
}
9416
9417
23.4k
inline size_t url_search_params::size() const noexcept { return params.size(); }
9418
9419
inline std::optional<std::string_view> url_search_params::get(
9420
17.6k
    const std::string_view key) {
9421
17.6k
  auto entry = std::ranges::find_if(
9422
123k
      params, [&key](const auto& param) { return param.first == key; });
9423
9424
17.6k
  if (entry == params.end()) {
9425
4.89k
    return std::nullopt;
9426
4.89k
  }
9427
9428
12.7k
  return entry->second;
9429
17.6k
}
9430
9431
inline std::vector<std::string> url_search_params::get_all(
9432
5.21k
    const std::string_view key) {
9433
5.21k
  std::vector<std::string> out{};
9434
9435
15.6k
  for (auto& param : params) {
9436
15.6k
    if (param.first == key) {
9437
5.91k
      out.emplace_back(param.second);
9438
5.91k
    }
9439
15.6k
  }
9440
9441
5.21k
  return out;
9442
5.21k
}
9443
9444
22.1k
inline bool url_search_params::has(const std::string_view key) noexcept {
9445
22.1k
  auto entry = std::ranges::find_if(
9446
229k
      params, [&key](const auto& param) { return param.first == key; });
9447
22.1k
  return entry != params.end();
9448
22.1k
}
9449
9450
inline bool url_search_params::has(std::string_view key,
9451
12.3k
                                   std::string_view value) noexcept {
9452
118k
  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
9453
118k
    return param.first == key && param.second == value;
9454
118k
  });
9455
12.3k
  return entry != params.end();
9456
12.3k
}
9457
9458
23.4k
inline std::string url_search_params::to_string() const {
9459
23.4k
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9460
23.4k
  std::string out{};
9461
79.5k
  for (size_t i = 0; i < params.size(); i++) {
9462
56.1k
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9463
56.1k
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9464
9465
    // Performance optimization: Move this inside percent_encode.
9466
56.1k
    std::ranges::replace(key, ' ', '+');
9467
56.1k
    std::ranges::replace(value, ' ', '+');
9468
9469
56.1k
    if (i != 0) {
9470
44.1k
      out += "&";
9471
44.1k
    }
9472
56.1k
    out.append(key);
9473
56.1k
    out += "=";
9474
56.1k
    out.append(value);
9475
56.1k
  }
9476
23.4k
  return out;
9477
23.4k
}
9478
9479
inline void url_search_params::set(const std::string_view key,
9480
2.60k
                                   const std::string_view value) {
9481
5.21k
  const auto find = [&key](const auto& param) { return param.first == key; };
9482
9483
2.60k
  auto it = std::ranges::find_if(params, find);
9484
9485
2.60k
  if (it == params.end()) {
9486
0
    params.emplace_back(key, value);
9487
2.60k
  } else {
9488
2.60k
    it->second = value;
9489
2.60k
    params.erase(std::remove_if(std::next(it), params.end(), find),
9490
2.60k
                 params.end());
9491
2.60k
  }
9492
2.60k
}
9493
9494
3.59k
inline void url_search_params::remove(const std::string_view key) {
9495
3.59k
  std::erase_if(params,
9496
7.86k
                [&key](const auto& param) { return param.first == key; });
9497
3.59k
}
9498
9499
inline void url_search_params::remove(const std::string_view key,
9500
3.59k
                                      const std::string_view value) {
9501
3.59k
  std::erase_if(params, [&key, &value](const auto& param) {
9502
2.84k
    return param.first == key && param.second == value;
9503
2.84k
  });
9504
3.59k
}
9505
9506
5.21k
inline void url_search_params::sort() {
9507
  // Keys are expected to be valid UTF-8, but percent_decode can produce
9508
  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
9509
5.21k
  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
9510
37.9k
                                      const key_value_pair& rhs) {
9511
37.9k
    size_t i = 0, j = 0;
9512
37.9k
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9513
61.3k
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9514
39.2k
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9515
37.4k
      uint32_t codePoint1 = 0, codePoint2 = 0;
9516
9517
37.4k
      if (low_surrogate1 != 0) {
9518
949
        codePoint1 = low_surrogate1;
9519
949
        low_surrogate1 = 0;
9520
36.5k
      } else {
9521
36.5k
        uint8_t c1 = uint8_t(lhs.first[i]);
9522
36.5k
        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
9523
1.98k
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9524
1.98k
          i += 2;
9525
34.5k
        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
9526
1.00k
          codePoint1 = ((c1 & 0x0F) << 12) |
9527
1.00k
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9528
1.00k
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9529
1.00k
          i += 3;
9530
33.5k
        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
9531
1.26k
          codePoint1 = ((c1 & 0x07) << 18) |
9532
1.26k
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9533
1.26k
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9534
1.26k
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9535
1.26k
          i += 4;
9536
9537
1.26k
          codePoint1 -= 0x10000;
9538
1.26k
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9539
1.26k
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9540
1.26k
          codePoint1 = high_surrogate;
9541
32.2k
        } else {
9542
          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
9543
32.2k
          codePoint1 = c1;
9544
32.2k
          i++;
9545
32.2k
        }
9546
36.5k
      }
9547
9548
37.4k
      if (low_surrogate2 != 0) {
9549
950
        codePoint2 = low_surrogate2;
9550
950
        low_surrogate2 = 0;
9551
36.5k
      } else {
9552
36.5k
        uint8_t c2 = uint8_t(rhs.first[j]);
9553
36.5k
        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
9554
2.21k
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
9555
2.21k
          j += 2;
9556
34.2k
        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
9557
1.03k
          codePoint2 = ((c2 & 0x0F) << 12) |
9558
1.03k
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
9559
1.03k
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
9560
1.03k
          j += 3;
9561
33.2k
        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
9562
1.41k
          codePoint2 = ((c2 & 0x07) << 18) |
9563
1.41k
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
9564
1.41k
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
9565
1.41k
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
9566
1.41k
          j += 4;
9567
1.41k
          codePoint2 -= 0x10000;
9568
1.41k
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
9569
1.41k
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
9570
1.41k
          codePoint2 = high_surrogate;
9571
31.8k
        } else {
9572
          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
9573
31.8k
          codePoint2 = c2;
9574
31.8k
          j++;
9575
31.8k
        }
9576
36.5k
      }
9577
9578
37.4k
      if (codePoint1 != codePoint2) {
9579
14.1k
        return (codePoint1 < codePoint2);
9580
14.1k
      }
9581
37.4k
    }
9582
23.8k
    return (j < rhs.first.size() || low_surrogate2 != 0);
9583
37.9k
  });
9584
5.21k
}
9585
9586
2.60k
inline url_search_params_keys_iter url_search_params::get_keys() {
9587
2.60k
  return url_search_params_keys_iter(*this);
9588
2.60k
}
9589
9590
/**
9591
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9592
 */
9593
2.60k
inline url_search_params_values_iter url_search_params::get_values() {
9594
2.60k
  return url_search_params_values_iter(*this);
9595
2.60k
}
9596
9597
/**
9598
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9599
 */
9600
2.60k
inline url_search_params_entries_iter url_search_params::get_entries() {
9601
2.60k
  return url_search_params_entries_iter(*this);
9602
2.60k
}
9603
9604
template <typename T, url_search_params_iter_type Type>
9605
59.8k
inline bool url_search_params_iter<T, Type>::has_next() const {
9606
59.8k
  return pos < params.params.size();
9607
59.8k
}
ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::has_next() const
Line
Count
Source
9605
19.9k
inline bool url_search_params_iter<T, Type>::has_next() const {
9606
19.9k
  return pos < params.params.size();
9607
19.9k
}
ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::has_next() const
Line
Count
Source
9605
19.9k
inline bool url_search_params_iter<T, Type>::has_next() const {
9606
19.9k
  return pos < params.params.size();
9607
19.9k
}
ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::has_next() const
Line
Count
Source
9605
19.9k
inline bool url_search_params_iter<T, Type>::has_next() const {
9606
19.9k
  return pos < params.params.size();
9607
19.9k
}
9608
9609
template <>
9610
8.67k
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
9611
8.67k
  if (!has_next()) {
9612
0
    return std::nullopt;
9613
0
  }
9614
8.67k
  return params.params[pos++].first;
9615
8.67k
}
9616
9617
template <>
9618
8.67k
inline std::optional<std::string_view> url_search_params_values_iter::next() {
9619
8.67k
  if (!has_next()) {
9620
0
    return std::nullopt;
9621
0
  }
9622
8.67k
  return params.params[pos++].second;
9623
8.67k
}
9624
9625
template <>
9626
inline std::optional<key_value_view_pair>
9627
8.67k
url_search_params_entries_iter::next() {
9628
8.67k
  if (!has_next()) {
9629
0
    return std::nullopt;
9630
0
  }
9631
8.67k
  return params.params[pos++];
9632
8.67k
}
9633
9634
}  // namespace ada
9635
9636
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
9637
/* end file include/ada/url_search_params-inl.h */
9638
9639
/* begin file include/ada/url_pattern-inl.h */
9640
/**
9641
 * @file url_pattern-inl.h
9642
 * @brief Declaration for the URLPattern inline functions.
9643
 */
9644
#ifndef ADA_URL_PATTERN_INL_H
9645
#define ADA_URL_PATTERN_INL_H
9646
9647
9648
#include <algorithm>
9649
#include <string_view>
9650
#include <utility>
9651
9652
#if ADA_INCLUDE_URL_PATTERN
9653
namespace ada {
9654
9655
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
9656
0
  return protocol == other.protocol && username == other.username &&
9657
0
         password == other.password && hostname == other.hostname &&
9658
0
         port == other.port && search == other.search && hash == other.hash &&
9659
0
         pathname == other.pathname;
9660
0
}
9661
9662
inline bool url_pattern_component_result::operator==(
9663
0
    const url_pattern_component_result& other) const {
9664
0
  return input == other.input && groups == other.groups;
9665
0
}
9666
9667
template <url_pattern_regex::regex_concept regex_provider>
9668
url_pattern_component_result
9669
url_pattern_component<regex_provider>::create_component_match_result(
9670
    std::string&& input,
9671
    std::vector<std::optional<std::string>>&& exec_result) {
9672
  // Let result be a new URLPatternComponentResult.
9673
  // Set result["input"] to input.
9674
  // Let groups be a record<USVString, (USVString or undefined)>.
9675
  auto result =
9676
      url_pattern_component_result{.input = std::move(input), .groups = {}};
9677
9678
  // We explicitly start iterating from 0 even though the spec
9679
  // says we should start from 1. This case is handled by the
9680
  // std_regex_provider which removes the full match from index 0.
9681
  // Use min() to guard against potential mismatches between
9682
  // exec_result size and group_name_list size.
9683
  const size_t size = std::min(exec_result.size(), group_name_list.size());
9684
  result.groups.reserve(size);
9685
  for (size_t index = 0; index < size; index++) {
9686
    result.groups.emplace(group_name_list[index],
9687
                          std::move(exec_result[index]));
9688
  }
9689
  return result;
9690
}
9691
9692
template <url_pattern_regex::regex_concept regex_provider>
9693
std::string_view url_pattern<regex_provider>::get_protocol() const
9694
    ada_lifetime_bound {
9695
  // Return this's associated URL pattern's protocol component's pattern string.
9696
  return protocol_component.pattern;
9697
}
9698
template <url_pattern_regex::regex_concept regex_provider>
9699
std::string_view url_pattern<regex_provider>::get_username() const
9700
    ada_lifetime_bound {
9701
  // Return this's associated URL pattern's username component's pattern string.
9702
  return username_component.pattern;
9703
}
9704
template <url_pattern_regex::regex_concept regex_provider>
9705
std::string_view url_pattern<regex_provider>::get_password() const
9706
    ada_lifetime_bound {
9707
  // Return this's associated URL pattern's password component's pattern string.
9708
  return password_component.pattern;
9709
}
9710
template <url_pattern_regex::regex_concept regex_provider>
9711
std::string_view url_pattern<regex_provider>::get_hostname() const
9712
    ada_lifetime_bound {
9713
  // Return this's associated URL pattern's hostname component's pattern string.
9714
  return hostname_component.pattern;
9715
}
9716
template <url_pattern_regex::regex_concept regex_provider>
9717
std::string_view url_pattern<regex_provider>::get_port() const
9718
    ada_lifetime_bound {
9719
  // Return this's associated URL pattern's port component's pattern string.
9720
  return port_component.pattern;
9721
}
9722
template <url_pattern_regex::regex_concept regex_provider>
9723
std::string_view url_pattern<regex_provider>::get_pathname() const
9724
    ada_lifetime_bound {
9725
  // Return this's associated URL pattern's pathname component's pattern string.
9726
  return pathname_component.pattern;
9727
}
9728
template <url_pattern_regex::regex_concept regex_provider>
9729
std::string_view url_pattern<regex_provider>::get_search() const
9730
    ada_lifetime_bound {
9731
  // Return this's associated URL pattern's search component's pattern string.
9732
  return search_component.pattern;
9733
}
9734
template <url_pattern_regex::regex_concept regex_provider>
9735
std::string_view url_pattern<regex_provider>::get_hash() const
9736
    ada_lifetime_bound {
9737
  // Return this's associated URL pattern's hash component's pattern string.
9738
  return hash_component.pattern;
9739
}
9740
template <url_pattern_regex::regex_concept regex_provider>
9741
bool url_pattern<regex_provider>::ignore_case() const {
9742
  return ignore_case_;
9743
}
9744
template <url_pattern_regex::regex_concept regex_provider>
9745
bool url_pattern<regex_provider>::has_regexp_groups() const {
9746
  // If this's associated URL pattern's has regexp groups, then return true.
9747
  return protocol_component.has_regexp_groups ||
9748
         username_component.has_regexp_groups ||
9749
         password_component.has_regexp_groups ||
9750
         hostname_component.has_regexp_groups ||
9751
         port_component.has_regexp_groups ||
9752
         pathname_component.has_regexp_groups ||
9753
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
9754
}
9755
9756
0
inline bool url_pattern_part::is_regexp() const noexcept {
9757
0
  return type == url_pattern_part_type::REGEXP;
9758
0
}
9759
9760
inline std::string_view url_pattern_compile_component_options::get_delimiter()
9761
0
    const {
9762
0
  if (delimiter) {
9763
0
    return {&delimiter.value(), 1};
9764
0
  }
9765
0
  return {};
9766
0
}
9767
9768
inline std::string_view url_pattern_compile_component_options::get_prefix()
9769
0
    const {
9770
0
  if (prefix) {
9771
0
    return {&prefix.value(), 1};
9772
0
  }
9773
0
  return {};
9774
0
}
9775
9776
template <url_pattern_regex::regex_concept regex_provider>
9777
template <url_pattern_encoding_callback F>
9778
tl::expected<url_pattern_component<regex_provider>, errors>
9779
url_pattern_component<regex_provider>::compile(
9780
    std::string_view input, F& encoding_callback,
9781
    url_pattern_compile_component_options& options) {
9782
  ada_log("url_pattern_component::compile input: ", input);
9783
  // Let part list be the result of running parse a pattern string given input,
9784
  // options, and encoding callback.
9785
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
9786
                                                             encoding_callback);
9787
9788
  if (!part_list) {
9789
    ada_log("parse_pattern_string failed");
9790
    return tl::unexpected(part_list.error());
9791
  }
9792
9793
  // Detect pattern type early to potentially skip expensive regex compilation
9794
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
9795
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
9796
9797
  url_pattern_component_type component_type =
9798
      url_pattern_component_type::REGEXP;
9799
  std::string exact_match_value{};
9800
9801
  if (part_list->empty()) {
9802
    component_type = url_pattern_component_type::EMPTY;
9803
  } else if (part_list->size() == 1) {
9804
    const auto& part = (*part_list)[0];
9805
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
9806
        part.modifier == url_pattern_part_modifier::none &&
9807
        !options.ignore_case) {
9808
      component_type = url_pattern_component_type::EXACT_MATCH;
9809
      exact_match_value = part.value;
9810
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
9811
               part.modifier == url_pattern_part_modifier::none &&
9812
               part.prefix.empty() && part.suffix.empty()) {
9813
      component_type = url_pattern_component_type::FULL_WILDCARD;
9814
    }
9815
  }
9816
9817
  // For simple patterns, skip regex generation and compilation entirely
9818
  if (component_type != url_pattern_component_type::REGEXP) {
9819
    auto pattern_string =
9820
        url_pattern_helpers::generate_pattern_string(*part_list, options);
9821
    // For FULL_WILDCARD, we need the group name from
9822
    // generate_regular_expression
9823
    std::vector<std::string> name_list;
9824
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
9825
        !part_list->empty()) {
9826
      name_list.push_back((*part_list)[0].name);
9827
    }
9828
    return url_pattern_component<regex_provider>(
9829
        std::move(pattern_string), typename regex_provider::regex_type{},
9830
        std::move(name_list), has_regexp_groups, component_type,
9831
        std::move(exact_match_value));
9832
  }
9833
9834
  // Generate regex for complex patterns
9835
  auto [regular_expression_string, name_list] =
9836
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
9837
                                                                     options);
9838
  auto pattern_string =
9839
      url_pattern_helpers::generate_pattern_string(*part_list, options);
9840
9841
  std::optional<typename regex_provider::regex_type> regular_expression =
9842
      regex_provider::create_instance(regular_expression_string,
9843
                                      options.ignore_case);
9844
  if (!regular_expression) {
9845
    return tl::unexpected(errors::type_error);
9846
  }
9847
9848
  return url_pattern_component<regex_provider>(
9849
      std::move(pattern_string), std::move(*regular_expression),
9850
      std::move(name_list), has_regexp_groups, component_type,
9851
      std::move(exact_match_value));
9852
}
9853
9854
template <url_pattern_regex::regex_concept regex_provider>
9855
bool url_pattern_component<regex_provider>::fast_test(
9856
    std::string_view input) const noexcept {
9857
  // Fast path for simple patterns - avoid regex evaluation
9858
  // Using if-else for better branch prediction on common cases
9859
  if (type == url_pattern_component_type::FULL_WILDCARD) {
9860
    return true;
9861
  }
9862
  if (type == url_pattern_component_type::EXACT_MATCH) {
9863
    return input == exact_match_value;
9864
  }
9865
  if (type == url_pattern_component_type::EMPTY) {
9866
    return input.empty();
9867
  }
9868
  // type == REGEXP
9869
  return regex_provider::regex_match(input, regexp);
9870
}
9871
9872
template <url_pattern_regex::regex_concept regex_provider>
9873
std::optional<std::vector<std::optional<std::string>>>
9874
url_pattern_component<regex_provider>::fast_match(
9875
    std::string_view input) const {
9876
  // Handle each type directly without redundant checks
9877
  if (type == url_pattern_component_type::FULL_WILDCARD) {
9878
    // FULL_WILDCARD always matches - capture the input (even if empty)
9879
    // If there's no group name, return empty groups
9880
    if (group_name_list.empty()) {
9881
      return std::vector<std::optional<std::string>>{};
9882
    }
9883
    // Capture the matched input (including empty strings)
9884
    return std::vector<std::optional<std::string>>{std::string(input)};
9885
  }
9886
  if (type == url_pattern_component_type::EXACT_MATCH) {
9887
    if (input == exact_match_value) {
9888
      return std::vector<std::optional<std::string>>{};
9889
    }
9890
    return std::nullopt;
9891
  }
9892
  if (type == url_pattern_component_type::EMPTY) {
9893
    if (input.empty()) {
9894
      return std::vector<std::optional<std::string>>{};
9895
    }
9896
    return std::nullopt;
9897
  }
9898
  // type == REGEXP - use regex
9899
  return regex_provider::regex_search(input, regexp);
9900
}
9901
9902
template <url_pattern_regex::regex_concept regex_provider>
9903
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::exec(
9904
    const url_pattern_input& input, const std::string_view* base_url) {
9905
  // Return the result of match given this's associated URL pattern, input, and
9906
  // baseURL if given.
9907
  return match(input, base_url);
9908
}
9909
9910
template <url_pattern_regex::regex_concept regex_provider>
9911
bool url_pattern<regex_provider>::test_components(
9912
    std::string_view protocol, std::string_view username,
9913
    std::string_view password, std::string_view hostname, std::string_view port,
9914
    std::string_view pathname, std::string_view search,
9915
    std::string_view hash) const {
9916
  return protocol_component.fast_test(protocol) &&
9917
         username_component.fast_test(username) &&
9918
         password_component.fast_test(password) &&
9919
         hostname_component.fast_test(hostname) &&
9920
         port_component.fast_test(port) &&
9921
         pathname_component.fast_test(pathname) &&
9922
         search_component.fast_test(search) && hash_component.fast_test(hash);
9923
}
9924
9925
template <url_pattern_regex::regex_concept regex_provider>
9926
result<bool> url_pattern<regex_provider>::test(
9927
    const url_pattern_input& input, const std::string_view* base_url_string) {
9928
  // If input is a URLPatternInit
9929
  if (std::holds_alternative<url_pattern_init>(input)) {
9930
    if (base_url_string) {
9931
      return tl::unexpected(errors::type_error);
9932
    }
9933
9934
    std::string protocol{}, username{}, password{}, hostname{};
9935
    std::string port{}, pathname{}, search{}, hash{};
9936
9937
    auto apply_result = url_pattern_init::process(
9938
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
9939
        protocol, username, password, hostname, port, pathname, search, hash);
9940
9941
    if (!apply_result) {
9942
      return false;
9943
    }
9944
9945
    std::string_view search_view = *apply_result->search;
9946
    if (search_view.starts_with("?")) {
9947
      search_view.remove_prefix(1);
9948
    }
9949
9950
    return test_components(*apply_result->protocol, *apply_result->username,
9951
                           *apply_result->password, *apply_result->hostname,
9952
                           *apply_result->port, *apply_result->pathname,
9953
                           search_view, *apply_result->hash);
9954
  }
9955
9956
  // URL string input path
9957
  result<url_aggregator> base_url;
9958
  if (base_url_string) {
9959
    base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
9960
    if (!base_url) {
9961
      return false;
9962
    }
9963
  }
9964
9965
  auto url =
9966
      ada::parse<url_aggregator>(std::get<std::string_view>(input),
9967
                                 base_url.has_value() ? &*base_url : nullptr);
9968
  if (!url) {
9969
    return false;
9970
  }
9971
9972
  // Extract components as string_view
9973
  auto protocol_view = url->get_protocol();
9974
  if (protocol_view.ends_with(":")) {
9975
    protocol_view.remove_suffix(1);
9976
  }
9977
9978
  auto search_view = url->get_search();
9979
  if (search_view.starts_with("?")) {
9980
    search_view.remove_prefix(1);
9981
  }
9982
9983
  auto hash_view = url->get_hash();
9984
  if (hash_view.starts_with("#")) {
9985
    hash_view.remove_prefix(1);
9986
  }
9987
9988
  return test_components(protocol_view, url->get_username(),
9989
                         url->get_password(), url->get_hostname(),
9990
                         url->get_port(), url->get_pathname(), search_view,
9991
                         hash_view);
9992
}
9993
9994
template <url_pattern_regex::regex_concept regex_provider>
9995
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::match(
9996
    const url_pattern_input& input, const std::string_view* base_url_string) {
9997
  std::string protocol{};
9998
  std::string username{};
9999
  std::string password{};
10000
  std::string hostname{};
10001
  std::string port{};
10002
  std::string pathname{};
10003
  std::string search{};
10004
  std::string hash{};
10005
10006
  // Let inputs be an empty list.
10007
  // Append input to inputs.
10008
  std::vector inputs{input};
10009
10010
  // If input is a URLPatternInit then:
10011
  if (std::holds_alternative<url_pattern_init>(input)) {
10012
    ada_log(
10013
        "url_pattern::match called with url_pattern_init and base_url_string=",
10014
        base_url_string);
10015
    // If baseURLString was given, throw a TypeError.
10016
    if (base_url_string) {
10017
      ada_log("failed to match because base_url_string was given");
10018
      return tl::unexpected(errors::type_error);
10019
    }
10020
10021
    // Let applyResult be the result of process a URLPatternInit given input,
10022
    // "url", protocol, username, password, hostname, port, pathname, search,
10023
    // and hash.
10024
    auto apply_result = url_pattern_init::process(
10025
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10026
        protocol, username, password, hostname, port, pathname, search, hash);
10027
10028
    // If this throws an exception, catch it, and return null.
10029
    if (!apply_result.has_value()) {
10030
      ada_log("match returned std::nullopt because process threw");
10031
      return std::nullopt;
10032
    }
10033
10034
    // Set protocol to applyResult["protocol"].
10035
    ADA_ASSERT_TRUE(apply_result->protocol.has_value());
10036
    protocol = std::move(apply_result->protocol.value());
10037
10038
    // Set username to applyResult["username"].
10039
    ADA_ASSERT_TRUE(apply_result->username.has_value());
10040
    username = std::move(apply_result->username.value());
10041
10042
    // Set password to applyResult["password"].
10043
    ADA_ASSERT_TRUE(apply_result->password.has_value());
10044
    password = std::move(apply_result->password.value());
10045
10046
    // Set hostname to applyResult["hostname"].
10047
    ADA_ASSERT_TRUE(apply_result->hostname.has_value());
10048
    hostname = std::move(apply_result->hostname.value());
10049
10050
    // Set port to applyResult["port"].
10051
    ADA_ASSERT_TRUE(apply_result->port.has_value());
10052
    port = std::move(apply_result->port.value());
10053
10054
    // Set pathname to applyResult["pathname"].
10055
    ADA_ASSERT_TRUE(apply_result->pathname.has_value());
10056
    pathname = std::move(apply_result->pathname.value());
10057
10058
    // Set search to applyResult["search"].
10059
    ADA_ASSERT_TRUE(apply_result->search.has_value());
10060
    if (apply_result->search->starts_with("?")) {
10061
      search = apply_result->search->substr(1);
10062
    } else {
10063
      search = std::move(apply_result->search.value());
10064
    }
10065
10066
    // Set hash to applyResult["hash"].
10067
    ADA_ASSERT_TRUE(apply_result->hash.has_value());
10068
    ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#"));
10069
    hash = std::move(apply_result->hash.value());
10070
  } else {
10071
    ADA_ASSERT_TRUE(std::holds_alternative<std::string_view>(input));
10072
10073
    // Let baseURL be null.
10074
    result<url_aggregator> base_url;
10075
10076
    // If baseURLString was given, then:
10077
    if (base_url_string) {
10078
      // Let baseURL be the result of parsing baseURLString.
10079
      base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10080
10081
      // If baseURL is failure, return null.
10082
      if (!base_url) {
10083
        ada_log("match returned std::nullopt because failed to parse base_url=",
10084
                *base_url_string);
10085
        return std::nullopt;
10086
      }
10087
10088
      // Append baseURLString to inputs.
10089
      inputs.emplace_back(*base_url_string);
10090
    }
10091
10092
    url_aggregator* base_url_value =
10093
        base_url.has_value() ? &*base_url : nullptr;
10094
10095
    // Set url to the result of parsing input given baseURL.
10096
    auto url = ada::parse<url_aggregator>(std::get<std::string_view>(input),
10097
                                          base_url_value);
10098
10099
    // If url is failure, return null.
10100
    if (!url) {
10101
      ada_log("match returned std::nullopt because url failed");
10102
      return std::nullopt;
10103
    }
10104
10105
    // Set protocol to url's scheme.
10106
    // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':'
10107
    // is removed. Similar work was done on workerd:
10108
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038
10109
    protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1);
10110
    // Set username to url's username.
10111
    username = url->get_username();
10112
    // Set password to url's password.
10113
    password = url->get_password();
10114
    // Set hostname to url's host, serialized, or the empty string if the value
10115
    // is null.
10116
    hostname = url->get_hostname();
10117
    // Set port to url's port, serialized, or the empty string if the value is
10118
    // null.
10119
    port = url->get_port();
10120
    // Set pathname to the result of URL path serializing url.
10121
    pathname = url->get_pathname();
10122
    // Set search to url's query or the empty string if the value is null.
10123
    // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?'
10124
    // is removed. Similar work was done on workerd:
10125
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232
10126
    if (url->has_search()) {
10127
      auto view = url->get_search();
10128
      search = view.starts_with("?") ? url->get_search().substr(1) : view;
10129
    }
10130
    // Set hash to url's fragment or the empty string if the value is null.
10131
    // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is
10132
    // removed. Similar work was done on workerd:
10133
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242
10134
    if (url->has_hash()) {
10135
      auto view = url->get_hash();
10136
      hash = view.starts_with("#") ? url->get_hash().substr(1) : view;
10137
    }
10138
  }
10139
10140
  // Use fast_match which skips regex for simple patterns (EMPTY, EXACT_MATCH,
10141
  // FULL_WILDCARD) and only falls back to regex for complex REGEXP patterns.
10142
10143
  // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol
10144
  // component's regular expression, protocol).
10145
  auto protocol_exec_result = protocol_component.fast_match(protocol);
10146
  if (!protocol_exec_result) {
10147
    return std::nullopt;
10148
  }
10149
10150
  // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username
10151
  // component's regular expression, username).
10152
  auto username_exec_result = username_component.fast_match(username);
10153
  if (!username_exec_result) {
10154
    return std::nullopt;
10155
  }
10156
10157
  // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password
10158
  // component's regular expression, password).
10159
  auto password_exec_result = password_component.fast_match(password);
10160
  if (!password_exec_result) {
10161
    return std::nullopt;
10162
  }
10163
10164
  // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname
10165
  // component's regular expression, hostname).
10166
  auto hostname_exec_result = hostname_component.fast_match(hostname);
10167
  if (!hostname_exec_result) {
10168
    return std::nullopt;
10169
  }
10170
10171
  // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's
10172
  // regular expression, port).
10173
  auto port_exec_result = port_component.fast_match(port);
10174
  if (!port_exec_result) {
10175
    return std::nullopt;
10176
  }
10177
10178
  // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname
10179
  // component's regular expression, pathname).
10180
  auto pathname_exec_result = pathname_component.fast_match(pathname);
10181
  if (!pathname_exec_result) {
10182
    return std::nullopt;
10183
  }
10184
10185
  // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's
10186
  // regular expression, search).
10187
  auto search_exec_result = search_component.fast_match(search);
10188
  if (!search_exec_result) {
10189
    return std::nullopt;
10190
  }
10191
10192
  // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's
10193
  // regular expression, hash).
10194
  auto hash_exec_result = hash_component.fast_match(hash);
10195
  if (!hash_exec_result) {
10196
    return std::nullopt;
10197
  }
10198
10199
  // Let result be a new URLPatternResult.
10200
  auto result = url_pattern_result{};
10201
  // Set result["inputs"] to inputs.
10202
  result.inputs = std::move(inputs);
10203
  // Set result["protocol"] to the result of creating a component match result
10204
  // given urlPattern's protocol component, protocol, and protocolExecResult.
10205
  result.protocol = protocol_component.create_component_match_result(
10206
      std::move(protocol), std::move(*protocol_exec_result));
10207
10208
  // Set result["username"] to the result of creating a component match result
10209
  // given urlPattern's username component, username, and usernameExecResult.
10210
  result.username = username_component.create_component_match_result(
10211
      std::move(username), std::move(*username_exec_result));
10212
10213
  // Set result["password"] to the result of creating a component match result
10214
  // given urlPattern's password component, password, and passwordExecResult.
10215
  result.password = password_component.create_component_match_result(
10216
      std::move(password), std::move(*password_exec_result));
10217
10218
  // Set result["hostname"] to the result of creating a component match result
10219
  // given urlPattern's hostname component, hostname, and hostnameExecResult.
10220
  result.hostname = hostname_component.create_component_match_result(
10221
      std::move(hostname), std::move(*hostname_exec_result));
10222
10223
  // Set result["port"] to the result of creating a component match result given
10224
  // urlPattern's port component, port, and portExecResult.
10225
  result.port = port_component.create_component_match_result(
10226
      std::move(port), std::move(*port_exec_result));
10227
10228
  // Set result["pathname"] to the result of creating a component match result
10229
  // given urlPattern's pathname component, pathname, and pathnameExecResult.
10230
  result.pathname = pathname_component.create_component_match_result(
10231
      std::move(pathname), std::move(*pathname_exec_result));
10232
10233
  // Set result["search"] to the result of creating a component match result
10234
  // given urlPattern's search component, search, and searchExecResult.
10235
  result.search = search_component.create_component_match_result(
10236
      std::move(search), std::move(*search_exec_result));
10237
10238
  // Set result["hash"] to the result of creating a component match result given
10239
  // urlPattern's hash component, hash, and hashExecResult.
10240
  result.hash = hash_component.create_component_match_result(
10241
      std::move(hash), std::move(*hash_exec_result));
10242
10243
  return result;
10244
}
10245
10246
}  // namespace ada
10247
#endif  // ADA_INCLUDE_URL_PATTERN
10248
#endif
10249
/* end file include/ada/url_pattern-inl.h */
10250
/* begin file include/ada/url_pattern_helpers-inl.h */
10251
/**
10252
 * @file url_pattern_helpers-inl.h
10253
 * @brief Declaration for the URLPattern helpers.
10254
 */
10255
#ifndef ADA_URL_PATTERN_HELPERS_INL_H
10256
#define ADA_URL_PATTERN_HELPERS_INL_H
10257
10258
#include <optional>
10259
#include <string_view>
10260
10261
10262
#if ADA_INCLUDE_URL_PATTERN
10263
namespace ada::url_pattern_helpers {
10264
#if defined(ADA_TESTING) || defined(ADA_LOGGING)
10265
0
inline std::string to_string(token_type type) {
10266
0
  switch (type) {
10267
0
    case token_type::INVALID_CHAR:
10268
0
      return "INVALID_CHAR";
10269
0
    case token_type::OPEN:
10270
0
      return "OPEN";
10271
0
    case token_type::CLOSE:
10272
0
      return "CLOSE";
10273
0
    case token_type::REGEXP:
10274
0
      return "REGEXP";
10275
0
    case token_type::NAME:
10276
0
      return "NAME";
10277
0
    case token_type::CHAR:
10278
0
      return "CHAR";
10279
0
    case token_type::ESCAPED_CHAR:
10280
0
      return "ESCAPED_CHAR";
10281
0
    case token_type::OTHER_MODIFIER:
10282
0
      return "OTHER_MODIFIER";
10283
0
    case token_type::ASTERISK:
10284
0
      return "ASTERISK";
10285
0
    case token_type::END:
10286
0
      return "END";
10287
0
    default:
10288
0
      ada::unreachable();
10289
0
  }
10290
0
}
10291
#endif  // defined(ADA_TESTING) || defined(ADA_LOGGING)
10292
10293
template <url_pattern_regex::regex_concept regex_provider>
10294
constexpr void constructor_string_parser<regex_provider>::rewind() {
10295
  // Set parser's token index to parser's component start.
10296
  token_index = component_start;
10297
  // Set parser's token increment to 0.
10298
  token_increment = 0;
10299
}
10300
10301
template <url_pattern_regex::regex_concept regex_provider>
10302
constexpr bool constructor_string_parser<regex_provider>::is_hash_prefix() {
10303
  // Return the result of running is a non-special pattern char given parser,
10304
  // parser's token index and "#".
10305
  return is_non_special_pattern_char(token_index, '#');
10306
}
10307
10308
template <url_pattern_regex::regex_concept regex_provider>
10309
constexpr bool constructor_string_parser<regex_provider>::is_search_prefix() {
10310
  // If result of running is a non-special pattern char given parser, parser's
10311
  // token index and "?" is true, then return true.
10312
  if (is_non_special_pattern_char(token_index, '?')) {
10313
    return true;
10314
  }
10315
10316
  // If parser's token list[parser's token index]'s value is not "?", then
10317
  // return false.
10318
  if (token_list[token_index].value != "?") {
10319
    return false;
10320
  }
10321
10322
  // If previous index is less than 0, then return true.
10323
  if (token_index == 0) return true;
10324
  // Let previous index be parser's token index - 1.
10325
  auto previous_index = token_index - 1;
10326
  // Let previous token be the result of running get a safe token given parser
10327
  // and previous index.
10328
  auto previous_token = get_safe_token(previous_index);
10329
  ADA_ASSERT_TRUE(previous_token);
10330
  // If any of the following are true, then return false:
10331
  // - previous token's type is "name".
10332
  // - previous token's type is "regexp".
10333
  // - previous token's type is "close".
10334
  // - previous token's type is "asterisk".
10335
  return !(previous_token->type == token_type::NAME ||
10336
           previous_token->type == token_type::REGEXP ||
10337
           previous_token->type == token_type::CLOSE ||
10338
           previous_token->type == token_type::ASTERISK);
10339
}
10340
10341
template <url_pattern_regex::regex_concept regex_provider>
10342
constexpr bool
10343
constructor_string_parser<regex_provider>::is_non_special_pattern_char(
10344
    size_t index, uint32_t value) const {
10345
  // Let token be the result of running get a safe token given parser and index.
10346
  auto token = get_safe_token(index);
10347
  ADA_ASSERT_TRUE(token);
10348
10349
  // If token's value is not value, then return false.
10350
  // TODO: Remove this once we make sure get_safe_token returns a non-empty
10351
  // string.
10352
  if (!token->value.empty() &&
10353
      static_cast<uint32_t>(token->value[0]) != value) {
10354
    return false;
10355
  }
10356
10357
  // If any of the following are true:
10358
  // - token's type is "char";
10359
  // - token's type is "escaped-char"; or
10360
  // - token's type is "invalid-char",
10361
  // - then return true.
10362
  return token->type == token_type::CHAR ||
10363
         token->type == token_type::ESCAPED_CHAR ||
10364
         token->type == token_type::INVALID_CHAR;
10365
}
10366
10367
template <url_pattern_regex::regex_concept regex_provider>
10368
constexpr const token*
10369
constructor_string_parser<regex_provider>::get_safe_token(size_t index) const {
10370
  // If index is less than parser's token list's size, then return parser's
10371
  // token list[index].
10372
  if (index < token_list.size()) [[likely]] {
10373
    return &token_list[index];
10374
  }
10375
10376
  // Assert: parser's token list's size is greater than or equal to 1.
10377
  ADA_ASSERT_TRUE(!token_list.empty());
10378
10379
  // Let token be parser's token list[last index].
10380
  // Assert: token's type is "end".
10381
  ADA_ASSERT_TRUE(token_list.back().type == token_type::END);
10382
10383
  // Return token.
10384
  return &token_list.back();
10385
}
10386
10387
template <url_pattern_regex::regex_concept regex_provider>
10388
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10389
    const {
10390
  // If parser's token list[parser's token index]'s type is "open", then return
10391
  // true.
10392
  return token_list[token_index].type == token_type::OPEN;
10393
}
10394
10395
template <url_pattern_regex::regex_concept regex_provider>
10396
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10397
    const {
10398
  // If parser's token list[parser's token index]'s type is "close", then return
10399
  // true.
10400
  return token_list[token_index].type == token_type::CLOSE;
10401
}
10402
10403
template <url_pattern_regex::regex_concept regex_provider>
10404
constexpr bool
10405
constructor_string_parser<regex_provider>::next_is_authority_slashes() const {
10406
  // If the result of running is a non-special pattern char given parser,
10407
  // parser's token index + 1, and "/" is false, then return false.
10408
  if (!is_non_special_pattern_char(token_index + 1, '/')) {
10409
    return false;
10410
  }
10411
  // If the result of running is a non-special pattern char given parser,
10412
  // parser's token index + 2, and "/" is false, then return false.
10413
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10414
    return false;
10415
  }
10416
  return true;
10417
}
10418
10419
template <url_pattern_regex::regex_concept regex_provider>
10420
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10421
    const {
10422
  // Return the result of running is a non-special pattern char given parser,
10423
  // parser's token index, and ":".
10424
  return is_non_special_pattern_char(token_index, ':');
10425
}
10426
10427
template <url_pattern_regex::regex_concept regex_provider>
10428
void constructor_string_parser<regex_provider>::change_state(State new_state,
10429
                                                             size_t skip) {
10430
  // If parser's state is not "init", not "authority", and not "done", then set
10431
  // parser's result[parser's state] to the result of running make a component
10432
  // string given parser.
10433
  if (state != State::INIT && state != State::AUTHORITY &&
10434
      state != State::DONE) {
10435
    auto value = make_component_string();
10436
    // TODO: Simplify this.
10437
    switch (state) {
10438
      case State::PROTOCOL: {
10439
        result.protocol = value;
10440
        break;
10441
      }
10442
      case State::USERNAME: {
10443
        result.username = value;
10444
        break;
10445
      }
10446
      case State::PASSWORD: {
10447
        result.password = value;
10448
        break;
10449
      }
10450
      case State::HOSTNAME: {
10451
        result.hostname = value;
10452
        break;
10453
      }
10454
      case State::PORT: {
10455
        result.port = value;
10456
        break;
10457
      }
10458
      case State::PATHNAME: {
10459
        result.pathname = value;
10460
        break;
10461
      }
10462
      case State::SEARCH: {
10463
        result.search = value;
10464
        break;
10465
      }
10466
      case State::HASH: {
10467
        result.hash = value;
10468
        break;
10469
      }
10470
      default:
10471
        ada::unreachable();
10472
    }
10473
  }
10474
10475
  // If parser's state is not "init" and new state is not "done", then:
10476
  if (state != State::INIT && new_state != State::DONE) {
10477
    // If parser's state is "protocol", "authority", "username", or "password";
10478
    // new state is "port", "pathname", "search", or "hash"; and parser's
10479
    // result["hostname"] does not exist, then set parser's result["hostname"]
10480
    // to the empty string.
10481
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10482
         state == State::USERNAME || state == State::PASSWORD) &&
10483
        (new_state == State::PORT || new_state == State::PATHNAME ||
10484
         new_state == State::SEARCH || new_state == State::HASH) &&
10485
        !result.hostname)
10486
      result.hostname = "";
10487
  }
10488
10489
  // If parser's state is "protocol", "authority", "username", "password",
10490
  // "hostname", or "port"; new state is "search" or "hash"; and parser's
10491
  // result["pathname"] does not exist, then:
10492
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10493
       state == State::USERNAME || state == State::PASSWORD ||
10494
       state == State::HOSTNAME || state == State::PORT) &&
10495
      (new_state == State::SEARCH || new_state == State::HASH) &&
10496
      !result.pathname) {
10497
    if (protocol_matches_a_special_scheme_flag) {
10498
      result.pathname = "/";
10499
    } else {
10500
      // Otherwise, set parser's result["pathname"] to the empty string.
10501
      result.pathname = "";
10502
    }
10503
  }
10504
10505
  // If parser's state is "protocol", "authority", "username", "password",
10506
  // "hostname", "port", or "pathname"; new state is "hash"; and parser's
10507
  // result["search"] does not exist, then set parser's result["search"] to
10508
  // the empty string.
10509
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10510
       state == State::USERNAME || state == State::PASSWORD ||
10511
       state == State::HOSTNAME || state == State::PORT ||
10512
       state == State::PATHNAME) &&
10513
      new_state == State::HASH && !result.search) {
10514
    result.search = "";
10515
  }
10516
10517
  // Set parser's state to new state.
10518
  state = new_state;
10519
  // Increment parser's token index by skip.
10520
  token_index += skip;
10521
  // Set parser's component start to parser's token index.
10522
  component_start = token_index;
10523
  // Set parser's token increment to 0.
10524
  token_increment = 0;
10525
}
10526
10527
template <url_pattern_regex::regex_concept regex_provider>
10528
std::string constructor_string_parser<regex_provider>::make_component_string() {
10529
  // Assert: parser's token index is less than parser's token list's size.
10530
  ADA_ASSERT_TRUE(token_index < token_list.size());
10531
10532
  // Let token be parser's token list[parser's token index].
10533
  // Let end index be token's index.
10534
  const auto end_index = token_list[token_index].index;
10535
  // Let component start token be the result of running get a safe token given
10536
  // parser and parser's component start.
10537
  const auto component_start_token = get_safe_token(component_start);
10538
  ADA_ASSERT_TRUE(component_start_token);
10539
  // Let component start input index be component start token's index.
10540
  const auto component_start_input_index = component_start_token->index;
10541
  // Return the code point substring from component start input index to end
10542
  // index within parser's input.
10543
  return std::string(input.substr(component_start_input_index,
10544
                                  end_index - component_start_input_index));
10545
}
10546
10547
template <url_pattern_regex::regex_concept regex_provider>
10548
constexpr bool
10549
constructor_string_parser<regex_provider>::is_an_identity_terminator() const {
10550
  // Return the result of running is a non-special pattern char given parser,
10551
  // parser's token index, and "@".
10552
  return is_non_special_pattern_char(token_index, '@');
10553
}
10554
10555
template <url_pattern_regex::regex_concept regex_provider>
10556
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
10557
    const {
10558
  // Return the result of running is a non-special pattern char given parser,
10559
  // parser's token index, and "/".
10560
  return is_non_special_pattern_char(token_index, '/');
10561
}
10562
10563
template <url_pattern_regex::regex_concept regex_provider>
10564
constexpr bool constructor_string_parser<regex_provider>::is_password_prefix()
10565
    const {
10566
  // Return the result of running is a non-special pattern char given parser,
10567
  // parser's token index, and ":".
10568
  return is_non_special_pattern_char(token_index, ':');
10569
}
10570
10571
template <url_pattern_regex::regex_concept regex_provider>
10572
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_open()
10573
    const {
10574
  // Return the result of running is a non-special pattern char given parser,
10575
  // parser's token index, and "[".
10576
  return is_non_special_pattern_char(token_index, '[');
10577
}
10578
10579
template <url_pattern_regex::regex_concept regex_provider>
10580
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
10581
    const {
10582
  // Return the result of running is a non-special pattern char given parser,
10583
  // parser's token index, and "]".
10584
  return is_non_special_pattern_char(token_index, ']');
10585
}
10586
10587
template <url_pattern_regex::regex_concept regex_provider>
10588
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
10589
    const {
10590
  // Return the result of running is a non-special pattern char given parser,
10591
  // parser's token index, and ":".
10592
  return is_non_special_pattern_char(token_index, ':');
10593
}
10594
10595
0
constexpr void Tokenizer::get_next_code_point() {
10596
0
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
10597
0
  ADA_ASSERT_TRUE(next_index < input.size());
10598
  // Decode the next UTF-8 code point. If malformed or truncated, mark it as
10599
  // invalid, return the offending byte as the code point, and advance by one
10600
  // to guarantee forward progress.
10601
0
  invalid_code_point = false;
10602
0
  code_point = 0;
10603
0
  size_t number_bytes = 0;
10604
0
  const size_t initial_index = next_index;
10605
0
  unsigned char first_byte = input[next_index];
10606
10607
0
  if ((first_byte & 0x80) == 0) {
10608
    // 1-byte character (ASCII)
10609
0
    next_index++;
10610
0
    code_point = first_byte;
10611
0
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
10612
0
            uint32_t(code_point));
10613
0
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10614
0
            " input.size()=", input.size());
10615
0
    return;
10616
0
  }
10617
0
  ada_log("Tokenizer::get_next_code_point read first byte=",
10618
0
          uint32_t(first_byte));
10619
0
  if ((first_byte & 0xE0) == 0xC0) {
10620
0
    code_point = first_byte & 0x1F;
10621
0
    number_bytes = 2;
10622
0
    ada_log("Tokenizer::get_next_code_point two bytes");
10623
0
  } else if ((first_byte & 0xF0) == 0xE0) {
10624
0
    code_point = first_byte & 0x0F;
10625
0
    number_bytes = 3;
10626
0
    ada_log("Tokenizer::get_next_code_point three bytes");
10627
0
  } else if ((first_byte & 0xF8) == 0xF0) {
10628
0
    code_point = first_byte & 0x07;
10629
0
    number_bytes = 4;
10630
0
    ada_log("Tokenizer::get_next_code_point four bytes");
10631
0
  }
10632
10633
  // Invalid leading bytes that still match a multi-byte prefix.
10634
0
  if ((number_bytes == 2 && first_byte < 0xC2) ||
10635
0
      (number_bytes == 4 && first_byte > 0xF4)) {
10636
0
    invalid_code_point = true;
10637
0
    code_point = first_byte;
10638
0
    next_index = initial_index + 1;
10639
0
    return;
10640
0
  }
10641
10642
  // Invalid leading byte (e.g., continuation byte outside a sequence).
10643
0
  if (number_bytes == 0) {
10644
0
    invalid_code_point = true;
10645
0
    code_point = first_byte;
10646
0
    next_index = initial_index + 1;
10647
0
    return;
10648
0
  }
10649
10650
  // Truncated UTF-8 sequence.
10651
0
  if (number_bytes + next_index > input.size()) {
10652
0
    invalid_code_point = true;
10653
0
    code_point = first_byte;
10654
0
    next_index = initial_index + 1;
10655
0
    return;
10656
0
  }
10657
10658
0
  for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) {
10659
0
    unsigned char byte = input[i];
10660
0
    if ((byte & 0xC0) != 0x80) {
10661
0
      invalid_code_point = true;
10662
0
      code_point = first_byte;
10663
0
      next_index = initial_index + 1;
10664
0
      return;
10665
0
    }
10666
0
    ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte));
10667
0
    code_point = (code_point << 6) | (byte & 0x3F);
10668
0
  }
10669
0
  ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=",
10670
0
          uint32_t(code_point));
10671
0
  ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10672
0
          " input.size()=", input.size());
10673
0
  next_index += number_bytes;
10674
0
}
10675
10676
0
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
10677
0
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
10678
0
          new_index);
10679
  // Set tokenizer's next index to index.
10680
0
  next_index = new_index;
10681
  // Run get the next code point given tokenizer.
10682
0
  get_next_code_point();
10683
0
}
10684
10685
inline void Tokenizer::add_token(token_type type, size_t next_position,
10686
0
                                 size_t value_position, size_t value_length) {
10687
0
  ada_log("Tokenizer::add_token called with type=", to_string(type),
10688
0
          " next_position=", next_position, " value_position=", value_position);
10689
0
  ADA_ASSERT_TRUE(next_position >= value_position);
10690
10691
  // Let token be a new token.
10692
  // Set token's type to type.
10693
  // Set token's index to tokenizer's index.
10694
  // Set token's value to the code point substring from value position with
10695
  // length value length within tokenizer's input.
10696
  // Append token to the back of tokenizer's token list.
10697
0
  token_list.emplace_back(type, index,
10698
0
                          input.substr(value_position, value_length));
10699
  // Set tokenizer's index to next position.
10700
0
  index = next_position;
10701
0
}
10702
10703
inline void Tokenizer::add_token_with_default_length(token_type type,
10704
                                                     size_t next_position,
10705
0
                                                     size_t value_position) {
10706
  // Let computed length be next position - value position.
10707
0
  auto computed_length = next_position - value_position;
10708
  // Run add a token given tokenizer, type, next position, value position, and
10709
  // computed length.
10710
0
  add_token(type, next_position, value_position, computed_length);
10711
0
}
10712
10713
0
inline void Tokenizer::add_token_with_defaults(token_type type) {
10714
0
  ada_log("Tokenizer::add_token_with_defaults called with type=",
10715
0
          to_string(type));
10716
  // Run add a token with default length given tokenizer, type, tokenizer's next
10717
  // index, and tokenizer's index.
10718
0
  add_token_with_default_length(type, next_index, index);
10719
0
}
10720
10721
inline ada_warn_unused std::optional<errors>
10722
Tokenizer::process_tokenizing_error(size_t next_position,
10723
0
                                    size_t value_position) {
10724
  // If tokenizer's policy is "strict", then throw a TypeError.
10725
0
  if (policy == token_policy::strict) {
10726
0
    ada_log("process_tokenizing_error failed with next_position=",
10727
0
            next_position, " value_position=", value_position);
10728
0
    return errors::type_error;
10729
0
  }
10730
  // Assert: tokenizer's policy is "lenient".
10731
0
  ADA_ASSERT_TRUE(policy == token_policy::lenient);
10732
  // Run add a token with default length given tokenizer, "invalid-char", next
10733
  // position, and value position.
10734
0
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
10735
0
                                value_position);
10736
0
  return std::nullopt;
10737
0
}
10738
10739
template <url_pattern_encoding_callback F>
10740
token* url_pattern_parser<F>::try_consume_modifier_token() {
10741
  // Let token be the result of running try to consume a token given parser and
10742
  // "other-modifier".
10743
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
10744
  // If token is not null, then return token.
10745
  if (token) return token;
10746
  // Set token to the result of running try to consume a token given parser and
10747
  // "asterisk".
10748
  // Return token.
10749
  return try_consume_token(token_type::ASTERISK);
10750
}
10751
10752
template <url_pattern_encoding_callback F>
10753
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
10754
    const token* name_token) {
10755
  // Let token be the result of running try to consume a token given parser and
10756
  // "regexp".
10757
  auto token = try_consume_token(token_type::REGEXP);
10758
  // If name token is null and token is null, then set token to the result of
10759
  // running try to consume a token given parser and "asterisk".
10760
  if (!name_token && !token) {
10761
    token = try_consume_token(token_type::ASTERISK);
10762
  }
10763
  // Return token.
10764
  return token;
10765
}
10766
10767
template <url_pattern_encoding_callback F>
10768
token* url_pattern_parser<F>::try_consume_token(token_type type) {
10769
  ada_log("url_pattern_parser::try_consume_token called with type=",
10770
          to_string(type));
10771
  // Assert: parser's index is less than parser's token list size.
10772
  ADA_ASSERT_TRUE(index < tokens.size());
10773
  // Let next token be parser's token list[parser's index].
10774
  auto& next_token = tokens[index];
10775
  // If next token's type is not type return null.
10776
  if (next_token.type != type) return nullptr;
10777
  // Increase parser's index by 1.
10778
  index++;
10779
  // Return next token.
10780
  return &next_token;
10781
}
10782
10783
template <url_pattern_encoding_callback F>
10784
std::string url_pattern_parser<F>::consume_text() {
10785
  // Let result be the empty string.
10786
  std::string result{};
10787
  // While true:
10788
  while (true) {
10789
    // Let token be the result of running try to consume a token given parser
10790
    // and "char".
10791
    auto token = try_consume_token(token_type::CHAR);
10792
    // If token is null, then set token to the result of running try to consume
10793
    // a token given parser and "escaped-char".
10794
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
10795
    // If token is null, then break.
10796
    if (!token) break;
10797
    // Append token's value to the end of result.
10798
    result.append(token->value);
10799
  }
10800
  // Return result.
10801
  return result;
10802
}
10803
10804
template <url_pattern_encoding_callback F>
10805
bool url_pattern_parser<F>::consume_required_token(token_type type) {
10806
  ada_log("url_pattern_parser::consume_required_token called with type=",
10807
          to_string(type));
10808
  // Let result be the result of running try to consume a token given parser and
10809
  // type.
10810
  return try_consume_token(type) != nullptr;
10811
}
10812
10813
template <url_pattern_encoding_callback F>
10814
std::optional<errors>
10815
url_pattern_parser<F>::maybe_add_part_from_the_pending_fixed_value() {
10816
  // If parser's pending fixed value is the empty string, then return.
10817
  if (pending_fixed_value.empty()) {
10818
    ada_log("pending_fixed_value is empty");
10819
    return std::nullopt;
10820
  }
10821
  // Let encoded value be the result of running parser's encoding callback given
10822
  // parser's pending fixed value.
10823
  auto encoded_value = encoding_callback(pending_fixed_value);
10824
  if (!encoded_value) {
10825
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
10826
    return encoded_value.error();
10827
  }
10828
  // Set parser's pending fixed value to the empty string.
10829
  pending_fixed_value.clear();
10830
  // Let part be a new part whose type is "fixed-text", value is encoded value,
10831
  // and modifier is "none".
10832
  // Append part to parser's part list.
10833
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
10834
                     std::move(*encoded_value),
10835
                     url_pattern_part_modifier::none);
10836
  return std::nullopt;
10837
}
10838
10839
template <url_pattern_encoding_callback F>
10840
std::optional<errors> url_pattern_parser<F>::add_part(
10841
    std::string_view prefix, token* name_token, token* regexp_or_wildcard_token,
10842
    std::string_view suffix, token* modifier_token) {
10843
  // Let modifier be "none".
10844
  auto modifier = url_pattern_part_modifier::none;
10845
  // If modifier token is not null:
10846
  if (modifier_token) {
10847
    // If modifier token's value is "?" then set modifier to "optional".
10848
    if (modifier_token->value == "?") {
10849
      modifier = url_pattern_part_modifier::optional;
10850
    } else if (modifier_token->value == "*") {
10851
      // Otherwise if modifier token's value is "*" then set modifier to
10852
      // "zero-or-more".
10853
      modifier = url_pattern_part_modifier::zero_or_more;
10854
    } else if (modifier_token->value == "+") {
10855
      // Otherwise if modifier token's value is "+" then set modifier to
10856
      // "one-or-more".
10857
      modifier = url_pattern_part_modifier::one_or_more;
10858
    }
10859
  }
10860
  // If name token is null and regexp or wildcard token is null and modifier
10861
  // is "none":
10862
  if (!name_token && !regexp_or_wildcard_token &&
10863
      modifier == url_pattern_part_modifier::none) {
10864
    // Append prefix to the end of parser's pending fixed value.
10865
    pending_fixed_value.append(prefix);
10866
    return std::nullopt;
10867
  }
10868
  // Run maybe add a part from the pending fixed value given parser.
10869
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
10870
    return *error;
10871
  }
10872
  // If name token is null and regexp or wildcard token is null:
10873
  if (!name_token && !regexp_or_wildcard_token) {
10874
    // Assert: suffix is the empty string.
10875
    ADA_ASSERT_TRUE(suffix.empty());
10876
    // If prefix is the empty string, then return.
10877
    if (prefix.empty()) return std::nullopt;
10878
    // Let encoded value be the result of running parser's encoding callback
10879
    // given prefix.
10880
    auto encoded_value = encoding_callback(prefix);
10881
    if (!encoded_value) {
10882
      return encoded_value.error();
10883
    }
10884
    // Let part be a new part whose type is "fixed-text", value is encoded
10885
    // value, and modifier is modifier.
10886
    // Append part to parser's part list.
10887
    parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
10888
                       std::move(*encoded_value), modifier);
10889
    return std::nullopt;
10890
  }
10891
  // Let regexp value be the empty string.
10892
  std::string regexp_value{};
10893
  // If regexp or wildcard token is null, then set regexp value to parser's
10894
  // segment wildcard regexp.
10895
  if (!regexp_or_wildcard_token) {
10896
    regexp_value = segment_wildcard_regexp;
10897
  } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) {
10898
    // Otherwise if regexp or wildcard token's type is "asterisk", then set
10899
    // regexp value to the full wildcard regexp value.
10900
    regexp_value = ".*";
10901
  } else {
10902
    // Otherwise set regexp value to regexp or wildcard token's value.
10903
    regexp_value = regexp_or_wildcard_token->value;
10904
  }
10905
  // Let type be "regexp".
10906
  auto type = url_pattern_part_type::REGEXP;
10907
  // If regexp value is parser's segment wildcard regexp:
10908
  if (regexp_value == segment_wildcard_regexp) {
10909
    // Set type to "segment-wildcard".
10910
    type = url_pattern_part_type::SEGMENT_WILDCARD;
10911
    // Set regexp value to the empty string.
10912
    regexp_value.clear();
10913
  } else if (regexp_value == ".*") {
10914
    // Otherwise if regexp value is the full wildcard regexp value:
10915
    // Set type to "full-wildcard".
10916
    type = url_pattern_part_type::FULL_WILDCARD;
10917
    // Set regexp value to the empty string.
10918
    regexp_value.clear();
10919
  }
10920
  // Let name be the empty string.
10921
  std::string name{};
10922
  // If name token is not null, then set name to name token's value.
10923
  if (name_token) {
10924
    name = name_token->value;
10925
  } else if (regexp_or_wildcard_token != nullptr) {
10926
    // Otherwise if regexp or wildcard token is not null:
10927
    // Set name to parser's next numeric name, serialized.
10928
    name = std::to_string(next_numeric_name);
10929
    // Increment parser's next numeric name by 1.
10930
    next_numeric_name++;
10931
  }
10932
  // If the result of running is a duplicate name given parser and name is
10933
  // true, then throw a TypeError.
10934
  if (std::ranges::any_of(
10935
          parts, [&name](const auto& part) { return part.name == name; })) {
10936
    return errors::type_error;
10937
  }
10938
  // Let encoded prefix be the result of running parser's encoding callback
10939
  // given prefix.
10940
  auto encoded_prefix = encoding_callback(prefix);
10941
  if (!encoded_prefix) return encoded_prefix.error();
10942
  // Let encoded suffix be the result of running parser's encoding callback
10943
  // given suffix.
10944
  auto encoded_suffix = encoding_callback(suffix);
10945
  if (!encoded_suffix) return encoded_suffix.error();
10946
  // Let part be a new part whose type is type, value is regexp value,
10947
  // modifier is modifier, name is name, prefix is encoded prefix, and suffix
10948
  // is encoded suffix.
10949
  // Append part to parser's part list.
10950
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
10951
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
10952
  return std::nullopt;
10953
}
10954
10955
template <url_pattern_encoding_callback F>
10956
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
10957
    std::string_view input, url_pattern_compile_component_options& options,
10958
    F& encoding_callback) {
10959
  ada_log("parse_pattern_string input=", input);
10960
  // Let parser be a new pattern parser whose encoding callback is encoding
10961
  // callback and segment wildcard regexp is the result of running generate a
10962
  // segment wildcard regexp given options.
10963
  auto parser = url_pattern_parser<F>(
10964
      encoding_callback, generate_segment_wildcard_regexp(options));
10965
  // Set parser's token list to the result of running tokenize given input and
10966
  // "strict".
10967
  auto tokenize_result = tokenize(input, token_policy::strict);
10968
  if (!tokenize_result) {
10969
    ada_log("parse_pattern_string tokenize failed");
10970
    return tl::unexpected(tokenize_result.error());
10971
  }
10972
  parser.tokens = std::move(*tokenize_result);
10973
10974
  // While parser's index is less than parser's token list's size:
10975
  while (parser.can_continue()) {
10976
    // Let char token be the result of running try to consume a token given
10977
    // parser and "char".
10978
    auto char_token = parser.try_consume_token(token_type::CHAR);
10979
    // Let name token be the result of running try to consume a token given
10980
    // parser and "name".
10981
    auto name_token = parser.try_consume_token(token_type::NAME);
10982
    // Let regexp or wildcard token be the result of running try to consume a
10983
    // regexp or wildcard token given parser and name token.
10984
    auto regexp_or_wildcard_token =
10985
        parser.try_consume_regexp_or_wildcard_token(name_token);
10986
    // If name token is not null or regexp or wildcard token is not null:
10987
    if (name_token || regexp_or_wildcard_token) {
10988
      // Let prefix be the empty string.
10989
      std::string prefix{};
10990
      // If char token is not null then set prefix to char token's value.
10991
      if (char_token) prefix = char_token->value;
10992
      // If prefix is not the empty string and not options's prefix code point:
10993
      if (!prefix.empty() && prefix != options.get_prefix()) {
10994
        // Append prefix to the end of parser's pending fixed value.
10995
        parser.pending_fixed_value.append(prefix);
10996
        // Set prefix to the empty string.
10997
        prefix.clear();
10998
      }
10999
      // Run maybe add a part from the pending fixed value given parser.
11000
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11001
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
11002
        return tl::unexpected(*error);
11003
      }
11004
      // Let modifier token be the result of running try to consume a modifier
11005
      // token given parser.
11006
      auto modifier_token = parser.try_consume_modifier_token();
11007
      // Run add a part given parser, prefix, name token, regexp or wildcard
11008
      // token, the empty string, and modifier token.
11009
      if (auto error =
11010
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
11011
                              modifier_token)) {
11012
        ada_log("parser.add_part failed");
11013
        return tl::unexpected(*error);
11014
      }
11015
      // Continue.
11016
      continue;
11017
    }
11018
11019
    // Let fixed token be char token.
11020
    auto fixed_token = char_token;
11021
    // If fixed token is null, then set fixed token to the result of running try
11022
    // to consume a token given parser and "escaped-char".
11023
    if (!fixed_token)
11024
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
11025
    // If fixed token is not null:
11026
    if (fixed_token) {
11027
      // Append fixed token's value to parser's pending fixed value.
11028
      parser.pending_fixed_value.append(fixed_token->value);
11029
      // Continue.
11030
      continue;
11031
    }
11032
    // Let open token be the result of running try to consume a token given
11033
    // parser and "open".
11034
    auto open_token = parser.try_consume_token(token_type::OPEN);
11035
    // If open token is not null:
11036
    if (open_token) {
11037
      // Set prefix be the result of running consume text given parser.
11038
      auto prefix_ = parser.consume_text();
11039
      // Set name token to the result of running try to consume a token given
11040
      // parser and "name".
11041
      name_token = parser.try_consume_token(token_type::NAME);
11042
      // Set regexp or wildcard token to the result of running try to consume a
11043
      // regexp or wildcard token given parser and name token.
11044
      regexp_or_wildcard_token =
11045
          parser.try_consume_regexp_or_wildcard_token(name_token);
11046
      // Let suffix be the result of running consume text given parser.
11047
      auto suffix_ = parser.consume_text();
11048
      // Run consume a required token given parser and "close".
11049
      if (!parser.consume_required_token(token_type::CLOSE)) {
11050
        ada_log("parser.consume_required_token failed");
11051
        return tl::unexpected(errors::type_error);
11052
      }
11053
      // Set modifier token to the result of running try to consume a modifier
11054
      // token given parser.
11055
      auto modifier_token = parser.try_consume_modifier_token();
11056
      // Run add a part given parser, prefix, name token, regexp or wildcard
11057
      // token, suffix, and modifier token.
11058
      if (auto error =
11059
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
11060
                              suffix_, modifier_token)) {
11061
        return tl::unexpected(*error);
11062
      }
11063
      // Continue.
11064
      continue;
11065
    }
11066
    // Run maybe add a part from the pending fixed value given parser.
11067
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11068
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
11069
      return tl::unexpected(*error);
11070
    }
11071
    // Run consume a required token given parser and "end".
11072
    if (!parser.consume_required_token(token_type::END)) {
11073
      return tl::unexpected(errors::type_error);
11074
    }
11075
  }
11076
  ada_log("parser.parts size is: ", parser.parts.size());
11077
  // Return parser's part list.
11078
  return parser.parts;
11079
}
11080
11081
template <url_pattern_regex::regex_concept regex_provider>
11082
bool protocol_component_matches_special_scheme(
11083
    url_pattern_component<regex_provider>& component) {
11084
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
11085
  switch (component.type) {
11086
    case url_pattern_component_type::EMPTY:
11087
      // Empty pattern can't match any special scheme
11088
      return false;
11089
    case url_pattern_component_type::EXACT_MATCH:
11090
      // Direct string comparison for exact match patterns
11091
      return component.exact_match_value == "http" ||
11092
             component.exact_match_value == "https" ||
11093
             component.exact_match_value == "ws" ||
11094
             component.exact_match_value == "wss" ||
11095
             component.exact_match_value == "ftp";
11096
    case url_pattern_component_type::FULL_WILDCARD:
11097
      // Full wildcard matches everything including special schemes
11098
      return true;
11099
    case url_pattern_component_type::REGEXP:
11100
      // Fall back to regex matching for complex patterns
11101
      auto& regex = component.regexp;
11102
      return regex_provider::regex_match("http", regex) ||
11103
             regex_provider::regex_match("https", regex) ||
11104
             regex_provider::regex_match("ws", regex) ||
11105
             regex_provider::regex_match("wss", regex) ||
11106
             regex_provider::regex_match("ftp", regex);
11107
  }
11108
  ada::unreachable();
11109
}
11110
11111
template <url_pattern_regex::regex_concept regex_provider>
11112
inline std::optional<errors> constructor_string_parser<
11113
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
11114
  ada_log(
11115
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
11116
      "flag");
11117
  // Let protocol string be the result of running make a component string given
11118
  // parser.
11119
  auto protocol_string = make_component_string();
11120
  // Let protocol component be the result of compiling a component given
11121
  // protocol string, canonicalize a protocol, and default options.
11122
  auto protocol_component = url_pattern_component<regex_provider>::compile(
11123
      protocol_string, canonicalize_protocol,
11124
      url_pattern_compile_component_options::DEFAULT);
11125
  if (!protocol_component) {
11126
    ada_log("url_pattern_component::compile failed for protocol_string ",
11127
            protocol_string);
11128
    return protocol_component.error();
11129
  }
11130
  // If the result of running protocol component matches a special scheme given
11131
  // protocol component is true, then set parser's protocol matches a special
11132
  // scheme flag to true.
11133
  if (protocol_component_matches_special_scheme(*protocol_component)) {
11134
    protocol_matches_a_special_scheme_flag = true;
11135
  }
11136
  return std::nullopt;
11137
}
11138
11139
template <url_pattern_regex::regex_concept regex_provider>
11140
tl::expected<url_pattern_init, errors>
11141
constructor_string_parser<regex_provider>::parse(std::string_view input) {
11142
  ada_log("constructor_string_parser::parse input=", input);
11143
  // Let parser be a new constructor string parser whose input is input and
11144
  // token list is the result of running tokenize given input and "lenient".
11145
  auto token_list = tokenize(input, token_policy::lenient);
11146
  if (!token_list) {
11147
    return tl::unexpected(token_list.error());
11148
  }
11149
  auto parser = constructor_string_parser(input, std::move(*token_list));
11150
11151
  // While parser's token index is less than parser's token list size:
11152
  while (parser.token_index < parser.token_list.size()) {
11153
    // Set parser's token increment to 1.
11154
    parser.token_increment = 1;
11155
11156
    // If parser's token list[parser's token index]'s type is "end" then:
11157
    if (parser.token_list[parser.token_index].type == token_type::END) {
11158
      // If parser's state is "init":
11159
      if (parser.state == State::INIT) {
11160
        // Run rewind given parser.
11161
        parser.rewind();
11162
        // If the result of running is a hash prefix given parser is true, then
11163
        // run change state given parser, "hash" and 1.
11164
        if (parser.is_hash_prefix()) {
11165
          parser.change_state(State::HASH, 1);
11166
        } else if (parser.is_search_prefix()) {
11167
          // Otherwise if the result of running is a search prefix given parser
11168
          // is true: Run change state given parser, "search" and 1.
11169
          parser.change_state(State::SEARCH, 1);
11170
        } else {
11171
          // Run change state given parser, "pathname" and 0.
11172
          parser.change_state(State::PATHNAME, 0);
11173
        }
11174
        // Increment parser's token index by parser's token increment.
11175
        parser.token_index += parser.token_increment;
11176
        // Continue.
11177
        continue;
11178
      }
11179
11180
      if (parser.state == State::AUTHORITY) {
11181
        // If parser's state is "authority":
11182
        // Run rewind and set state given parser, and "hostname".
11183
        parser.rewind();
11184
        parser.change_state(State::HOSTNAME, 0);
11185
        // Increment parser's token index by parser's token increment.
11186
        parser.token_index += parser.token_increment;
11187
        // Continue.
11188
        continue;
11189
      }
11190
11191
      // Run change state given parser, "done" and 0.
11192
      parser.change_state(State::DONE, 0);
11193
      // Break.
11194
      break;
11195
    }
11196
11197
    // If the result of running is a group open given parser is true:
11198
    if (parser.is_group_open()) {
11199
      // Increment parser's group depth by 1.
11200
      parser.group_depth += 1;
11201
      // Increment parser's token index by parser's token increment.
11202
      parser.token_index += parser.token_increment;
11203
    }
11204
11205
    // If parser's group depth is greater than 0:
11206
    if (parser.group_depth > 0) {
11207
      // If the result of running is a group close given parser is true, then
11208
      // decrement parser's group depth by 1.
11209
      if (parser.is_group_close()) {
11210
        parser.group_depth -= 1;
11211
      } else {
11212
        // Increment parser's token index by parser's token increment.
11213
        parser.token_index += parser.token_increment;
11214
        continue;
11215
      }
11216
    }
11217
11218
    // Switch on parser's state and run the associated steps:
11219
    switch (parser.state) {
11220
      case State::INIT: {
11221
        // If the result of running is a protocol suffix given parser is true:
11222
        if (parser.is_protocol_suffix()) {
11223
          // Run rewind and set state given parser and "protocol".
11224
          parser.rewind();
11225
          parser.change_state(State::PROTOCOL, 0);
11226
        }
11227
        break;
11228
      }
11229
      case State::PROTOCOL: {
11230
        // If the result of running is a protocol suffix given parser is true:
11231
        if (parser.is_protocol_suffix()) {
11232
          // Run compute protocol matches a special scheme flag given parser.
11233
          if (const auto error =
11234
                  parser.compute_protocol_matches_special_scheme_flag()) {
11235
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11236
            return tl::unexpected(*error);
11237
          }
11238
          // Let next state be "pathname".
11239
          auto next_state = State::PATHNAME;
11240
          // Let skip be 1.
11241
          auto skip = 1;
11242
          // If the result of running next is authority slashes given parser is
11243
          // true:
11244
          if (parser.next_is_authority_slashes()) {
11245
            // Set next state to "authority".
11246
            next_state = State::AUTHORITY;
11247
            // Set skip to 3.
11248
            skip = 3;
11249
          } else if (parser.protocol_matches_a_special_scheme_flag) {
11250
            // Otherwise if parser's protocol matches a special scheme flag is
11251
            // true, then set next state to "authority".
11252
            next_state = State::AUTHORITY;
11253
          }
11254
11255
          // Run change state given parser, next state, and skip.
11256
          parser.change_state(next_state, skip);
11257
        }
11258
        break;
11259
      }
11260
      case State::AUTHORITY: {
11261
        // If the result of running is an identity terminator given parser is
11262
        // true, then run rewind and set state given parser and "username".
11263
        if (parser.is_an_identity_terminator()) {
11264
          parser.rewind();
11265
          parser.change_state(State::USERNAME, 0);
11266
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11267
                   parser.is_hash_prefix()) {
11268
          // Otherwise if any of the following are true:
11269
          // - the result of running is a pathname start given parser;
11270
          // - the result of running is a search prefix given parser; or
11271
          // - the result of running is a hash prefix given parser,
11272
          // then run rewind and set state given parser and "hostname".
11273
          parser.rewind();
11274
          parser.change_state(State::HOSTNAME, 0);
11275
        }
11276
        break;
11277
      }
11278
      case State::USERNAME: {
11279
        // If the result of running is a password prefix given parser is true,
11280
        // then run change state given parser, "password", and 1.
11281
        if (parser.is_password_prefix()) {
11282
          parser.change_state(State::PASSWORD, 1);
11283
        } else if (parser.is_an_identity_terminator()) {
11284
          // Otherwise if the result of running is an identity terminator given
11285
          // parser is true, then run change state given parser, "hostname",
11286
          // and 1.
11287
          parser.change_state(State::HOSTNAME, 1);
11288
        }
11289
        break;
11290
      }
11291
      case State::PASSWORD: {
11292
        // If the result of running is an identity terminator given parser is
11293
        // true, then run change state given parser, "hostname", and 1.
11294
        if (parser.is_an_identity_terminator()) {
11295
          parser.change_state(State::HOSTNAME, 1);
11296
        }
11297
        break;
11298
      }
11299
      case State::HOSTNAME: {
11300
        // If the result of running is an IPv6 open given parser is true, then
11301
        // increment parser's hostname IPv6 bracket depth by 1.
11302
        if (parser.is_an_ipv6_open()) {
11303
          parser.hostname_ipv6_bracket_depth += 1;
11304
        } else if (parser.is_an_ipv6_close()) {
11305
          // Otherwise if the result of running is an IPv6 close given parser is
11306
          // true, then decrement parser's hostname IPv6 bracket depth by 1.
11307
          parser.hostname_ipv6_bracket_depth -= 1;
11308
        } else if (parser.is_port_prefix() &&
11309
                   parser.hostname_ipv6_bracket_depth == 0) {
11310
          // Otherwise if the result of running is a port prefix given parser is
11311
          // true and parser's hostname IPv6 bracket depth is zero, then run
11312
          // change state given parser, "port", and 1.
11313
          parser.change_state(State::PORT, 1);
11314
        } else if (parser.is_pathname_start()) {
11315
          // Otherwise if the result of running is a pathname start given parser
11316
          // is true, then run change state given parser, "pathname", and 0.
11317
          parser.change_state(State::PATHNAME, 0);
11318
        } else if (parser.is_search_prefix()) {
11319
          // Otherwise if the result of running is a search prefix given parser
11320
          // is true, then run change state given parser, "search", and 1.
11321
          parser.change_state(State::SEARCH, 1);
11322
        } else if (parser.is_hash_prefix()) {
11323
          // Otherwise if the result of running is a hash prefix given parser is
11324
          // true, then run change state given parser, "hash", and 1.
11325
          parser.change_state(State::HASH, 1);
11326
        }
11327
11328
        break;
11329
      }
11330
      case State::PORT: {
11331
        // If the result of running is a pathname start given parser is true,
11332
        // then run change state given parser, "pathname", and 0.
11333
        if (parser.is_pathname_start()) {
11334
          parser.change_state(State::PATHNAME, 0);
11335
        } else if (parser.is_search_prefix()) {
11336
          // Otherwise if the result of running is a search prefix given parser
11337
          // is true, then run change state given parser, "search", and 1.
11338
          parser.change_state(State::SEARCH, 1);
11339
        } else if (parser.is_hash_prefix()) {
11340
          // Otherwise if the result of running is a hash prefix given parser is
11341
          // true, then run change state given parser, "hash", and 1.
11342
          parser.change_state(State::HASH, 1);
11343
        }
11344
        break;
11345
      }
11346
      case State::PATHNAME: {
11347
        // If the result of running is a search prefix given parser is true,
11348
        // then run change state given parser, "search", and 1.
11349
        if (parser.is_search_prefix()) {
11350
          parser.change_state(State::SEARCH, 1);
11351
        } else if (parser.is_hash_prefix()) {
11352
          // Otherwise if the result of running is a hash prefix given parser is
11353
          // true, then run change state given parser, "hash", and 1.
11354
          parser.change_state(State::HASH, 1);
11355
        }
11356
        break;
11357
      }
11358
      case State::SEARCH: {
11359
        // If the result of running is a hash prefix given parser is true, then
11360
        // run change state given parser, "hash", and 1.
11361
        if (parser.is_hash_prefix()) {
11362
          parser.change_state(State::HASH, 1);
11363
        }
11364
        break;
11365
      }
11366
      case State::HASH: {
11367
        // Do nothing
11368
        break;
11369
      }
11370
      default: {
11371
        // Assert: This step is never reached.
11372
        unreachable();
11373
      }
11374
    }
11375
11376
    // Increment parser's token index by parser's token increment.
11377
    parser.token_index += parser.token_increment;
11378
  }
11379
11380
  // If parser's result contains "hostname" and not "port", then set parser's
11381
  // result["port"] to the empty string.
11382
  if (parser.result.hostname && !parser.result.port) {
11383
    parser.result.port = "";
11384
  }
11385
11386
  // Return parser's result.
11387
  return parser.result;
11388
}
11389
11390
}  // namespace ada::url_pattern_helpers
11391
#endif  // ADA_INCLUDE_URL_PATTERN
11392
#endif
11393
/* end file include/ada/url_pattern_helpers-inl.h */
11394
11395
// Public API
11396
/* begin file include/ada/ada_version.h */
11397
/**
11398
 * @file ada_version.h
11399
 * @brief Definitions for Ada's version number.
11400
 */
11401
#ifndef ADA_ADA_VERSION_H
11402
#define ADA_ADA_VERSION_H
11403
11404
0
#define ADA_VERSION "3.4.4"
11405
11406
namespace ada {
11407
11408
enum {
11409
  ADA_VERSION_MAJOR = 3,
11410
  ADA_VERSION_MINOR = 4,
11411
  ADA_VERSION_REVISION = 4,
11412
};
11413
11414
}  // namespace ada
11415
11416
#endif  // ADA_ADA_VERSION_H
11417
/* end file include/ada/ada_version.h */
11418
/* begin file include/ada/implementation-inl.h */
11419
/**
11420
 * @file implementation-inl.h
11421
 */
11422
#ifndef ADA_IMPLEMENTATION_INL_H
11423
#define ADA_IMPLEMENTATION_INL_H
11424
11425
11426
11427
#include <variant>
11428
#include <string_view>
11429
11430
namespace ada {
11431
11432
#if ADA_INCLUDE_URL_PATTERN
11433
template <url_pattern_regex::regex_concept regex_provider>
11434
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
11435
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
11436
                  const std::string_view* base_url,
11437
                  const url_pattern_options* options) {
11438
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
11439
                                                        base_url, options);
11440
}
11441
#endif  // ADA_INCLUDE_URL_PATTERN
11442
11443
}  // namespace ada
11444
11445
#endif  // ADA_IMPLEMENTATION_INL_H
11446
/* end file include/ada/implementation-inl.h */
11447
11448
#endif  // ADA_H
11449
/* end file include/ada.h */