From 61f5291d6729ea47d2416959327e467f4438feeb Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Fri, 20 Dec 2024 16:03:04 -0500 Subject: [PATCH] src: implement whatwg's URLPattern spec --- deps/ada/ada.cpp | 5350 ++++++++----- deps/ada/ada.gyp | 12 +- deps/ada/ada.h | 6870 ++++++++++++----- .../bootstrap/web/exposed-window-or-worker.js | 6 +- lib/internal/url.js | 2 + lib/url.js | 2 + node.gyp | 2 + src/env_properties.h | 9 + src/node_binding.cc | 2 + src/node_external_reference.h | 1 + src/node_url_pattern.cc | 708 ++ src/node_url_pattern.h | 104 + test/fixtures/wpt/README.md | 1 + test/fixtures/wpt/urlpattern/WEB_FEATURES.yml | 3 + .../urlpattern-compare-test-data.json | 152 + .../urlpattern-compare-tests.tentative.js | 26 + .../urlpattern-hasregexpgroups-tests.js | 18 + .../resources/urlpatterntestdata.json | 2857 +++++++ .../urlpattern/resources/urlpatterntests.js | 188 + .../urlpattern-compare.tentative.any.js | 2 + .../urlpattern-compare.tentative.https.any.js | 2 + .../urlpattern-hasregexpgroups.any.js | 2 + .../fixtures/wpt/urlpattern/urlpattern.any.js | 2 + .../wpt/urlpattern/urlpattern.https.any.js | 2 + test/fixtures/wpt/versions.json | 4 + test/parallel/test-bootstrap-modules.js | 1 + test/wpt/status/url.json | 11 +- test/wpt/status/urlpattern.json | 169 + test/wpt/test-urlpattern.js | 8 + typings/globals.d.ts | 2 + typings/internalBinding/url_pattern.d.ts | 20 + 31 files changed, 12775 insertions(+), 3763 deletions(-) create mode 100644 src/node_url_pattern.cc create mode 100644 src/node_url_pattern.h create mode 100644 test/fixtures/wpt/urlpattern/WEB_FEATURES.yml create mode 100644 test/fixtures/wpt/urlpattern/resources/urlpattern-compare-test-data.json create mode 100644 test/fixtures/wpt/urlpattern/resources/urlpattern-compare-tests.tentative.js create mode 100644 test/fixtures/wpt/urlpattern/resources/urlpattern-hasregexpgroups-tests.js create mode 100644 test/fixtures/wpt/urlpattern/resources/urlpatterntestdata.json create mode 100644 test/fixtures/wpt/urlpattern/resources/urlpatterntests.js create mode 100644 test/fixtures/wpt/urlpattern/urlpattern-compare.tentative.any.js create mode 100644 test/fixtures/wpt/urlpattern/urlpattern-compare.tentative.https.any.js create mode 100644 test/fixtures/wpt/urlpattern/urlpattern-hasregexpgroups.any.js create mode 100644 test/fixtures/wpt/urlpattern/urlpattern.any.js create mode 100644 test/fixtures/wpt/urlpattern/urlpattern.https.any.js create mode 100644 test/wpt/status/urlpattern.json create mode 100644 test/wpt/test-urlpattern.js create mode 100644 typings/internalBinding/url_pattern.d.ts diff --git a/deps/ada/ada.cpp b/deps/ada/ada.cpp index d7f9b3a92c5330..59930dd242edab 100644 --- a/deps/ada/ada.cpp +++ b/deps/ada/ada.cpp @@ -1,28 +1,29 @@ -/* auto-generated on 2024-09-02 20:07:32 -0400. Do not edit! */ +/* auto-generated on 2025-01-23 16:07:04 -0500. Do not edit! */ /* begin file src/ada.cpp */ #include "ada.h" /* begin file src/checkers.cpp */ #include +#include +#include namespace ada::checkers { -ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept { +ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept { // The string is not empty and does not contain upper case ASCII characters. // // Optimization. To be considered as a possible ipv4, the string must end // with 'x' or a lowercase hex character. // Most of the time, this will be false so this simple check will save a lot // of effort. - char last_char = view.back(); // If the address ends with a dot, we need to prune it (special case). - if (last_char == '.') { + if (view.ends_with('.')) { view.remove_suffix(1); if (view.empty()) { return false; } - last_char = view.back(); } + char last_char = view.back(); bool possible_ipv4 = (last_char >= '0' && last_char <= '9') || (last_char >= 'a' && last_char <= 'f') || last_char == 'x'; @@ -38,7 +39,7 @@ ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept { /** Optimization opportunity: we have basically identified the last number of the ipv4 if we return true here. We might as well parse it and have at least one number parsed when we get to parse_ipv4. */ - if (std::all_of(view.begin(), view.end(), ada::checkers::is_digit)) { + if (std::ranges::all_of(view, ada::checkers::is_digit)) { return true; } // It could be hex (0x), but not if there is a single character. @@ -46,7 +47,7 @@ ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept { return false; } // It must start with 0x. - if (!std::equal(view.begin(), view.begin() + 2, "0x")) { + if (!view.starts_with("0x")) { return false; } // We must allow "0x". @@ -62,7 +63,7 @@ ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept { // for use with path_signature, we include all characters that need percent // encoding. static constexpr std::array path_signature_table = - []() constexpr { + []() consteval { std::array result{}; for (size_t i = 0; i < 256; i++) { if (i <= 0x20 || i == 0x22 || i == 0x23 || i == 0x3c || i == 0x3e || @@ -133,7 +134,7 @@ ada_really_inline constexpr bool verify_dns_length( ADA_PUSH_DISABLE_ALL_WARNINGS /* begin file src/ada_idna.cpp */ -/* auto-generated on 2023-09-19 15:58:51 -0400. Do not edit! */ +/* auto-generated on 2024-12-18 09:44:34 -0500. Do not edit! */ /* begin file src/idna.cpp */ /* begin file src/unicode_transcoding.cpp */ @@ -325,7 +326,7 @@ size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) { #include /* begin file src/mapping_tables.cpp */ -// IDNA 15.0.0 +// IDNA 15.1.0 // clang-format off #ifndef ADA_IDNA_TABLES_H @@ -334,7 +335,7 @@ size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) { namespace ada::idna { -const uint32_t mappings[5164] = +const uint32_t mappings[5165] = { 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 32, 32, 776, 32, 772, 50, 51, 32, 769, @@ -387,7 +388,7 @@ const uint32_t mappings[5164] = 7727, 7729, 7731, 7733, 7735, 7737, 7739, 7741, 7743, 7745, 7747, 7749, 7751, 7753, 7755, 7757, 7759, 7761, 7763, 7765, 7767, 7769, 7771, 7773, 7775, 7777, 7779, 7781, 7783, 7785, 7787, 7789, 7791, 7793, 7795, 7797, 7799, 7801, 7803, 7805, 7807, 7809, - 7811, 7813, 7815, 7817, 7819, 7821, 7823, 7825, 7827, 7829, 97, 702, 115, 115, 7841, + 7811, 7813, 7815, 7817, 7819, 7821, 7823, 7825, 7827, 7829, 97, 702, 223, 7841, 7843, 7845, 7847, 7849, 7851, 7853, 7855, 7857, 7859, 7861, 7863, 7865, 7867, 7869, 7871, 7873, 7875, 7877, 7879, 7881, 7883, 7885, 7887, 7889, 7891, 7893, 7895, 7897, 7899, 7901, 7903, 7905, 7907, 7909, 7911, 7913, 7915, 7917, 7919, 7921, 7923, 7925, @@ -667,56 +668,56 @@ const uint32_t mappings[5164] = 125235, 125236, 125237, 125238, 125239, 125240, 125241, 125242, 125243, 125244, 125245, 125246, 125247, 125248, 125249, 125250, 125251, 1646, 1697, 1647, 48, 44, 49, 44, 50, 44, 51, 44, 52, 44, 53, 44, 54, 44, 55, 44, 56, 44, 57, 44, 12308, 115, - 12309, 119, 122, 104, 118, 115, 100, 112, 112, 118, 119, 99, 109, 114, 100, 106, - 12411, 12363, 12467, 12467, 23383, 21452, 22810, 35299, 20132, 26144, 28961, 21069, - 24460, 20877, 26032, 21021, 32066, 36009, 22768, 21561, 28436, 25237, 25429, 36938, - 25351, 25171, 31105, 31354, 21512, 28288, 30003, 21106, 21942, 37197, 12308, 26412, - 12309, 12308, 19977, 12309, 12308, 20108, 12309, 12308, 23433, 12309, 12308, 28857, - 12309, 12308, 25171, 12309, 12308, 30423, 12309, 12308, 21213, 12309, 12308, 25943, - 12309, 24471, 21487, 20029, 20024, 20033, 131362, 20320, 20411, 20482, 20602, 20633, - 20687, 13470, 132666, 20820, 20836, 20855, 132380, 13497, 20839, 132427, 20887, - 20900, 20172, 20908, 168415, 20995, 13535, 21051, 21062, 21111, 13589, 21253, 21254, - 21321, 21338, 21363, 21373, 21375, 133676, 28784, 21450, 21471, 133987, 21483, 21489, - 21510, 21662, 21560, 21576, 21608, 21666, 21750, 21776, 21843, 21859, 21892, 21931, - 21939, 21954, 22294, 22295, 22097, 22132, 22766, 22478, 22516, 22541, 22411, 22578, - 22577, 22700, 136420, 22770, 22775, 22790, 22818, 22882, 136872, 136938, 23020, - 23067, 23079, 23000, 23142, 14062, 23304, 23358, 137672, 23491, 23512, 23539, 138008, - 23551, 23558, 14209, 23648, 23744, 23693, 138724, 23875, 138726, 23918, 23915, 23932, - 24033, 24034, 14383, 24061, 24104, 24125, 24169, 14434, 139651, 14460, 24240, 24243, - 24246, 172946, 140081, 33281, 24354, 14535, 144056, 156122, 24418, 24427, 14563, - 24474, 24525, 24535, 24569, 24705, 14650, 14620, 141012, 24775, 24904, 24908, 24954, - 25010, 24996, 25007, 25054, 25115, 25181, 25265, 25300, 25424, 142092, 25405, 25340, - 25448, 25475, 25572, 142321, 25634, 25541, 25513, 14894, 25705, 25726, 25757, 25719, - 14956, 25964, 143370, 26083, 26360, 26185, 15129, 15112, 15076, 20882, 20885, 26368, - 26268, 32941, 17369, 26401, 26462, 26451, 144323, 15177, 26618, 26501, 26706, 144493, - 26766, 26655, 26900, 26946, 27043, 27114, 27304, 145059, 27355, 15384, 27425, 145575, - 27476, 15438, 27506, 27551, 27579, 146061, 138507, 146170, 27726, 146620, 27839, - 27853, 27751, 27926, 27966, 28009, 28024, 28037, 146718, 27956, 28207, 28270, 15667, - 28359, 147153, 28153, 28526, 147294, 147342, 28614, 28729, 28699, 15766, 28746, - 28797, 28791, 28845, 132389, 28997, 148067, 29084, 29224, 29264, 149000, 29312, - 29333, 149301, 149524, 29562, 29579, 16044, 29605, 16056, 29767, 29788, 29829, 29898, - 16155, 29988, 150582, 30014, 150674, 139679, 30224, 151457, 151480, 151620, 16380, - 16392, 151795, 151794, 151833, 151859, 30494, 30495, 30603, 16454, 16534, 152605, - 30798, 16611, 153126, 153242, 153285, 31211, 16687, 31306, 31311, 153980, 154279, - 16898, 154539, 31686, 31689, 16935, 154752, 31954, 17056, 31976, 31971, 32000, 155526, - 32099, 17153, 32199, 32258, 32325, 17204, 156200, 156231, 17241, 156377, 32634, - 156478, 32661, 32762, 156890, 156963, 32864, 157096, 32880, 144223, 17365, 32946, - 33027, 17419, 33086, 23221, 157607, 157621, 144275, 144284, 33284, 36766, 17515, - 33425, 33419, 33437, 21171, 33457, 33459, 33469, 33510, 158524, 33565, 33635, 33709, - 33571, 33725, 33767, 33619, 33738, 33740, 33756, 158774, 159083, 158933, 17707, - 34033, 34035, 34070, 160714, 34148, 159532, 17757, 17761, 159665, 159954, 17771, - 34384, 34407, 34409, 34473, 34440, 34574, 34530, 34600, 34667, 34694, 34785, 34817, - 17913, 34912, 161383, 35031, 35038, 17973, 35066, 13499, 161966, 162150, 18110, - 18119, 35488, 162984, 36011, 36033, 36123, 36215, 163631, 133124, 36299, 36284, - 36336, 133342, 36564, 165330, 165357, 37012, 37105, 37137, 165678, 37147, 37432, - 37591, 37592, 37500, 37881, 37909, 166906, 38283, 18837, 38327, 167287, 18918, 38595, - 23986, 38691, 168261, 168474, 19054, 19062, 38880, 168970, 19122, 169110, 38953, - 169398, 39138, 19251, 39209, 39335, 39362, 39422, 19406, 170800, 40000, 40189, 19662, - 19693, 40295, 172238, 19704, 172293, 172558, 172689, 19798, 40702, 40709, 40719, - 40726, 173568, + 12309, 119, 122, 104, 118, 115, 100, 115, 115, 112, 112, 118, 119, 99, 109, 114, + 100, 106, 12411, 12363, 12467, 12467, 23383, 21452, 22810, 35299, 20132, 26144, + 28961, 21069, 24460, 20877, 26032, 21021, 32066, 36009, 22768, 21561, 28436, 25237, + 25429, 36938, 25351, 25171, 31105, 31354, 21512, 28288, 30003, 21106, 21942, 37197, + 12308, 26412, 12309, 12308, 19977, 12309, 12308, 20108, 12309, 12308, 23433, 12309, + 12308, 28857, 12309, 12308, 25171, 12309, 12308, 30423, 12309, 12308, 21213, 12309, + 12308, 25943, 12309, 24471, 21487, 20029, 20024, 20033, 131362, 20320, 20411, 20482, + 20602, 20633, 20687, 13470, 132666, 20820, 20836, 20855, 132380, 13497, 20839, 132427, + 20887, 20900, 20172, 20908, 168415, 20995, 13535, 21051, 21062, 21111, 13589, 21253, + 21254, 21321, 21338, 21363, 21373, 21375, 133676, 28784, 21450, 21471, 133987, 21483, + 21489, 21510, 21662, 21560, 21576, 21608, 21666, 21750, 21776, 21843, 21859, 21892, + 21931, 21939, 21954, 22294, 22295, 22097, 22132, 22766, 22478, 22516, 22541, 22411, + 22578, 22577, 22700, 136420, 22770, 22775, 22790, 22818, 22882, 136872, 136938, + 23020, 23067, 23079, 23000, 23142, 14062, 23304, 23358, 137672, 23491, 23512, 23539, + 138008, 23551, 23558, 14209, 23648, 23744, 23693, 138724, 23875, 138726, 23918, + 23915, 23932, 24033, 24034, 14383, 24061, 24104, 24125, 24169, 14434, 139651, 14460, + 24240, 24243, 24246, 172946, 140081, 33281, 24354, 14535, 144056, 156122, 24418, + 24427, 14563, 24474, 24525, 24535, 24569, 24705, 14650, 14620, 141012, 24775, 24904, + 24908, 24954, 25010, 24996, 25007, 25054, 25115, 25181, 25265, 25300, 25424, 142092, + 25405, 25340, 25448, 25475, 25572, 142321, 25634, 25541, 25513, 14894, 25705, 25726, + 25757, 25719, 14956, 25964, 143370, 26083, 26360, 26185, 15129, 15112, 15076, 20882, + 20885, 26368, 26268, 32941, 17369, 26401, 26462, 26451, 144323, 15177, 26618, 26501, + 26706, 144493, 26766, 26655, 26900, 26946, 27043, 27114, 27304, 145059, 27355, 15384, + 27425, 145575, 27476, 15438, 27506, 27551, 27579, 146061, 138507, 146170, 27726, + 146620, 27839, 27853, 27751, 27926, 27966, 28009, 28024, 28037, 146718, 27956, 28207, + 28270, 15667, 28359, 147153, 28153, 28526, 147294, 147342, 28614, 28729, 28699, + 15766, 28746, 28797, 28791, 28845, 132389, 28997, 148067, 29084, 29224, 29264, 149000, + 29312, 29333, 149301, 149524, 29562, 29579, 16044, 29605, 16056, 29767, 29788, 29829, + 29898, 16155, 29988, 150582, 30014, 150674, 139679, 30224, 151457, 151480, 151620, + 16380, 16392, 151795, 151794, 151833, 151859, 30494, 30495, 30603, 16454, 16534, + 152605, 30798, 16611, 153126, 153242, 153285, 31211, 16687, 31306, 31311, 153980, + 154279, 16898, 154539, 31686, 31689, 16935, 154752, 31954, 17056, 31976, 31971, + 32000, 155526, 32099, 17153, 32199, 32258, 32325, 17204, 156200, 156231, 17241, + 156377, 32634, 156478, 32661, 32762, 156890, 156963, 32864, 157096, 32880, 144223, + 17365, 32946, 33027, 17419, 33086, 23221, 157607, 157621, 144275, 144284, 33284, + 36766, 17515, 33425, 33419, 33437, 21171, 33457, 33459, 33469, 33510, 158524, 33565, + 33635, 33709, 33571, 33725, 33767, 33619, 33738, 33740, 33756, 158774, 159083, 158933, + 17707, 34033, 34035, 34070, 160714, 34148, 159532, 17757, 17761, 159665, 159954, + 17771, 34384, 34407, 34409, 34473, 34440, 34574, 34530, 34600, 34667, 34694, 34785, + 34817, 17913, 34912, 161383, 35031, 35038, 17973, 35066, 13499, 161966, 162150, + 18110, 18119, 35488, 162984, 36011, 36033, 36123, 36215, 163631, 133124, 36299, + 36284, 36336, 133342, 36564, 165330, 165357, 37012, 37105, 37137, 165678, 37147, + 37432, 37591, 37592, 37500, 37881, 37909, 166906, 38283, 18837, 38327, 167287, 18918, + 38595, 23986, 38691, 168261, 168474, 19054, 19062, 38880, 168970, 19122, 169110, + 38953, 169398, 39138, 19251, 39209, 39335, 39362, 39422, 19406, 170800, 40000, 40189, + 19662, 19693, 40295, 172238, 19704, 172293, 172558, 172689, 19798, 40702, 40709, + 40719, 40726, 173568, }; -const uint32_t table[8000][2] = +const uint32_t table[8002][2] = { {0, 1}, {65, 16777219}, {66, 16777475}, {67, 16777731}, {68, 16777987}, {69, 16778243}, {70, 16778499}, {71, 16778755}, @@ -1128,150 +1129,150 @@ const uint32_t table[8000][2] = {7818, 16973315}, {7819, 1}, {7820, 16973571}, {7821, 1}, {7822, 16973827}, {7823, 1}, {7824, 16974083}, {7825, 1}, {7826, 16974339}, {7827, 1}, {7828, 16974595}, {7829, 1}, - {7834, 33752067}, {7835, 16967939}, {7836, 1}, {7838, 33752579}, - {7839, 1}, {7840, 16975875}, {7841, 1}, {7842, 16976131}, - {7843, 1}, {7844, 16976387}, {7845, 1}, {7846, 16976643}, - {7847, 1}, {7848, 16976899}, {7849, 1}, {7850, 16977155}, - {7851, 1}, {7852, 16977411}, {7853, 1}, {7854, 16977667}, - {7855, 1}, {7856, 16977923}, {7857, 1}, {7858, 16978179}, - {7859, 1}, {7860, 16978435}, {7861, 1}, {7862, 16978691}, - {7863, 1}, {7864, 16978947}, {7865, 1}, {7866, 16979203}, - {7867, 1}, {7868, 16979459}, {7869, 1}, {7870, 16979715}, - {7871, 1}, {7872, 16979971}, {7873, 1}, {7874, 16980227}, - {7875, 1}, {7876, 16980483}, {7877, 1}, {7878, 16980739}, - {7879, 1}, {7880, 16980995}, {7881, 1}, {7882, 16981251}, - {7883, 1}, {7884, 16981507}, {7885, 1}, {7886, 16981763}, - {7887, 1}, {7888, 16982019}, {7889, 1}, {7890, 16982275}, - {7891, 1}, {7892, 16982531}, {7893, 1}, {7894, 16982787}, - {7895, 1}, {7896, 16983043}, {7897, 1}, {7898, 16983299}, - {7899, 1}, {7900, 16983555}, {7901, 1}, {7902, 16983811}, - {7903, 1}, {7904, 16984067}, {7905, 1}, {7906, 16984323}, - {7907, 1}, {7908, 16984579}, {7909, 1}, {7910, 16984835}, - {7911, 1}, {7912, 16985091}, {7913, 1}, {7914, 16985347}, - {7915, 1}, {7916, 16985603}, {7917, 1}, {7918, 16985859}, - {7919, 1}, {7920, 16986115}, {7921, 1}, {7922, 16986371}, - {7923, 1}, {7924, 16986627}, {7925, 1}, {7926, 16986883}, - {7927, 1}, {7928, 16987139}, {7929, 1}, {7930, 16987395}, - {7931, 1}, {7932, 16987651}, {7933, 1}, {7934, 16987907}, - {7935, 1}, {7944, 16988163}, {7945, 16988419}, {7946, 16988675}, - {7947, 16988931}, {7948, 16989187}, {7949, 16989443}, {7950, 16989699}, - {7951, 16989955}, {7952, 1}, {7958, 2}, {7960, 16990211}, - {7961, 16990467}, {7962, 16990723}, {7963, 16990979}, {7964, 16991235}, - {7965, 16991491}, {7966, 2}, {7968, 1}, {7976, 16991747}, - {7977, 16992003}, {7978, 16992259}, {7979, 16992515}, {7980, 16992771}, - {7981, 16993027}, {7982, 16993283}, {7983, 16993539}, {7984, 1}, - {7992, 16993795}, {7993, 16994051}, {7994, 16994307}, {7995, 16994563}, - {7996, 16994819}, {7997, 16995075}, {7998, 16995331}, {7999, 16995587}, - {8000, 1}, {8006, 2}, {8008, 16995843}, {8009, 16996099}, - {8010, 16996355}, {8011, 16996611}, {8012, 16996867}, {8013, 16997123}, - {8014, 2}, {8016, 1}, {8024, 2}, {8025, 16997379}, - {8026, 2}, {8027, 16997635}, {8028, 2}, {8029, 16997891}, - {8030, 2}, {8031, 16998147}, {8032, 1}, {8040, 16998403}, - {8041, 16998659}, {8042, 16998915}, {8043, 16999171}, {8044, 16999427}, - {8045, 16999683}, {8046, 16999939}, {8047, 17000195}, {8048, 1}, + {7834, 33752067}, {7835, 16967939}, {7836, 1}, {7838, 16975363}, + {7839, 1}, {7840, 16975619}, {7841, 1}, {7842, 16975875}, + {7843, 1}, {7844, 16976131}, {7845, 1}, {7846, 16976387}, + {7847, 1}, {7848, 16976643}, {7849, 1}, {7850, 16976899}, + {7851, 1}, {7852, 16977155}, {7853, 1}, {7854, 16977411}, + {7855, 1}, {7856, 16977667}, {7857, 1}, {7858, 16977923}, + {7859, 1}, {7860, 16978179}, {7861, 1}, {7862, 16978435}, + {7863, 1}, {7864, 16978691}, {7865, 1}, {7866, 16978947}, + {7867, 1}, {7868, 16979203}, {7869, 1}, {7870, 16979459}, + {7871, 1}, {7872, 16979715}, {7873, 1}, {7874, 16979971}, + {7875, 1}, {7876, 16980227}, {7877, 1}, {7878, 16980483}, + {7879, 1}, {7880, 16980739}, {7881, 1}, {7882, 16980995}, + {7883, 1}, {7884, 16981251}, {7885, 1}, {7886, 16981507}, + {7887, 1}, {7888, 16981763}, {7889, 1}, {7890, 16982019}, + {7891, 1}, {7892, 16982275}, {7893, 1}, {7894, 16982531}, + {7895, 1}, {7896, 16982787}, {7897, 1}, {7898, 16983043}, + {7899, 1}, {7900, 16983299}, {7901, 1}, {7902, 16983555}, + {7903, 1}, {7904, 16983811}, {7905, 1}, {7906, 16984067}, + {7907, 1}, {7908, 16984323}, {7909, 1}, {7910, 16984579}, + {7911, 1}, {7912, 16984835}, {7913, 1}, {7914, 16985091}, + {7915, 1}, {7916, 16985347}, {7917, 1}, {7918, 16985603}, + {7919, 1}, {7920, 16985859}, {7921, 1}, {7922, 16986115}, + {7923, 1}, {7924, 16986371}, {7925, 1}, {7926, 16986627}, + {7927, 1}, {7928, 16986883}, {7929, 1}, {7930, 16987139}, + {7931, 1}, {7932, 16987395}, {7933, 1}, {7934, 16987651}, + {7935, 1}, {7944, 16987907}, {7945, 16988163}, {7946, 16988419}, + {7947, 16988675}, {7948, 16988931}, {7949, 16989187}, {7950, 16989443}, + {7951, 16989699}, {7952, 1}, {7958, 2}, {7960, 16989955}, + {7961, 16990211}, {7962, 16990467}, {7963, 16990723}, {7964, 16990979}, + {7965, 16991235}, {7966, 2}, {7968, 1}, {7976, 16991491}, + {7977, 16991747}, {7978, 16992003}, {7979, 16992259}, {7980, 16992515}, + {7981, 16992771}, {7982, 16993027}, {7983, 16993283}, {7984, 1}, + {7992, 16993539}, {7993, 16993795}, {7994, 16994051}, {7995, 16994307}, + {7996, 16994563}, {7997, 16994819}, {7998, 16995075}, {7999, 16995331}, + {8000, 1}, {8006, 2}, {8008, 16995587}, {8009, 16995843}, + {8010, 16996099}, {8011, 16996355}, {8012, 16996611}, {8013, 16996867}, + {8014, 2}, {8016, 1}, {8024, 2}, {8025, 16997123}, + {8026, 2}, {8027, 16997379}, {8028, 2}, {8029, 16997635}, + {8030, 2}, {8031, 16997891}, {8032, 1}, {8040, 16998147}, + {8041, 16998403}, {8042, 16998659}, {8043, 16998915}, {8044, 16999171}, + {8045, 16999427}, {8046, 16999683}, {8047, 16999939}, {8048, 1}, {8049, 16849923}, {8050, 1}, {8051, 16850179}, {8052, 1}, {8053, 16850435}, {8054, 1}, {8055, 16850691}, {8056, 1}, {8057, 16850947}, {8058, 1}, {8059, 16851203}, {8060, 1}, - {8061, 16851459}, {8062, 2}, {8064, 33777667}, {8065, 33778179}, - {8066, 33778691}, {8067, 33779203}, {8068, 33779715}, {8069, 33780227}, - {8070, 33780739}, {8071, 33781251}, {8072, 33777667}, {8073, 33778179}, - {8074, 33778691}, {8075, 33779203}, {8076, 33779715}, {8077, 33780227}, - {8078, 33780739}, {8079, 33781251}, {8080, 33781763}, {8081, 33782275}, - {8082, 33782787}, {8083, 33783299}, {8084, 33783811}, {8085, 33784323}, - {8086, 33784835}, {8087, 33785347}, {8088, 33781763}, {8089, 33782275}, - {8090, 33782787}, {8091, 33783299}, {8092, 33783811}, {8093, 33784323}, - {8094, 33784835}, {8095, 33785347}, {8096, 33785859}, {8097, 33786371}, - {8098, 33786883}, {8099, 33787395}, {8100, 33787907}, {8101, 33788419}, - {8102, 33788931}, {8103, 33789443}, {8104, 33785859}, {8105, 33786371}, - {8106, 33786883}, {8107, 33787395}, {8108, 33787907}, {8109, 33788419}, - {8110, 33788931}, {8111, 33789443}, {8112, 1}, {8114, 33789955}, - {8115, 33790467}, {8116, 33790979}, {8117, 2}, {8118, 1}, - {8119, 33791491}, {8120, 17014787}, {8121, 17015043}, {8122, 17012739}, - {8123, 16849923}, {8124, 33790467}, {8125, 33792515}, {8126, 16846851}, - {8127, 33792515}, {8128, 33793027}, {8129, 50570755}, {8130, 33794307}, - {8131, 33794819}, {8132, 33795331}, {8133, 2}, {8134, 1}, - {8135, 33795843}, {8136, 17019139}, {8137, 16850179}, {8138, 17017091}, - {8139, 16850435}, {8140, 33794819}, {8141, 50573827}, {8142, 50574595}, - {8143, 50575363}, {8144, 1}, {8147, 17021699}, {8148, 2}, - {8150, 1}, {8152, 17021955}, {8153, 17022211}, {8154, 17022467}, - {8155, 16850691}, {8156, 2}, {8157, 50577155}, {8158, 50577923}, - {8159, 50578691}, {8160, 1}, {8163, 17025027}, {8164, 1}, - {8168, 17025283}, {8169, 17025539}, {8170, 17025795}, {8171, 16851203}, - {8172, 17026051}, {8173, 50580739}, {8174, 50403587}, {8175, 17027075}, - {8176, 2}, {8178, 33804547}, {8179, 33805059}, {8180, 33805571}, - {8181, 2}, {8182, 1}, {8183, 33806083}, {8184, 17029379}, - {8185, 16850947}, {8186, 17027331}, {8187, 16851459}, {8188, 33805059}, - {8189, 33562883}, {8190, 33799939}, {8191, 2}, {8192, 16783875}, + {8061, 16851459}, {8062, 2}, {8064, 33777411}, {8065, 33777923}, + {8066, 33778435}, {8067, 33778947}, {8068, 33779459}, {8069, 33779971}, + {8070, 33780483}, {8071, 33780995}, {8072, 33777411}, {8073, 33777923}, + {8074, 33778435}, {8075, 33778947}, {8076, 33779459}, {8077, 33779971}, + {8078, 33780483}, {8079, 33780995}, {8080, 33781507}, {8081, 33782019}, + {8082, 33782531}, {8083, 33783043}, {8084, 33783555}, {8085, 33784067}, + {8086, 33784579}, {8087, 33785091}, {8088, 33781507}, {8089, 33782019}, + {8090, 33782531}, {8091, 33783043}, {8092, 33783555}, {8093, 33784067}, + {8094, 33784579}, {8095, 33785091}, {8096, 33785603}, {8097, 33786115}, + {8098, 33786627}, {8099, 33787139}, {8100, 33787651}, {8101, 33788163}, + {8102, 33788675}, {8103, 33789187}, {8104, 33785603}, {8105, 33786115}, + {8106, 33786627}, {8107, 33787139}, {8108, 33787651}, {8109, 33788163}, + {8110, 33788675}, {8111, 33789187}, {8112, 1}, {8114, 33789699}, + {8115, 33790211}, {8116, 33790723}, {8117, 2}, {8118, 1}, + {8119, 33791235}, {8120, 17014531}, {8121, 17014787}, {8122, 17012483}, + {8123, 16849923}, {8124, 33790211}, {8125, 33792259}, {8126, 16846851}, + {8127, 33792259}, {8128, 33792771}, {8129, 50570499}, {8130, 33794051}, + {8131, 33794563}, {8132, 33795075}, {8133, 2}, {8134, 1}, + {8135, 33795587}, {8136, 17018883}, {8137, 16850179}, {8138, 17016835}, + {8139, 16850435}, {8140, 33794563}, {8141, 50573571}, {8142, 50574339}, + {8143, 50575107}, {8144, 1}, {8147, 17021443}, {8148, 2}, + {8150, 1}, {8152, 17021699}, {8153, 17021955}, {8154, 17022211}, + {8155, 16850691}, {8156, 2}, {8157, 50576899}, {8158, 50577667}, + {8159, 50578435}, {8160, 1}, {8163, 17024771}, {8164, 1}, + {8168, 17025027}, {8169, 17025283}, {8170, 17025539}, {8171, 16851203}, + {8172, 17025795}, {8173, 50580483}, {8174, 50403587}, {8175, 17026819}, + {8176, 2}, {8178, 33804291}, {8179, 33804803}, {8180, 33805315}, + {8181, 2}, {8182, 1}, {8183, 33805827}, {8184, 17029123}, + {8185, 16850947}, {8186, 17027075}, {8187, 16851459}, {8188, 33804803}, + {8189, 33562883}, {8190, 33799683}, {8191, 2}, {8192, 16783875}, {8203, 0}, {8204, 1}, {8206, 2}, {8208, 1}, - {8209, 17029635}, {8210, 1}, {8215, 33807107}, {8216, 1}, + {8209, 17029379}, {8210, 1}, {8215, 33806851}, {8216, 1}, {8228, 2}, {8231, 1}, {8232, 2}, {8239, 16783875}, - {8240, 1}, {8243, 33807619}, {8244, 50585347}, {8245, 1}, - {8246, 33808899}, {8247, 50586627}, {8248, 1}, {8252, 33810179}, - {8253, 1}, {8254, 33810691}, {8255, 1}, {8263, 33811203}, - {8264, 33811715}, {8265, 33812227}, {8266, 1}, {8279, 67362051}, + {8240, 1}, {8243, 33807363}, {8244, 50585091}, {8245, 1}, + {8246, 33808643}, {8247, 50586371}, {8248, 1}, {8252, 33809923}, + {8253, 1}, {8254, 33810435}, {8255, 1}, {8263, 33810947}, + {8264, 33811459}, {8265, 33811971}, {8266, 1}, {8279, 67361795}, {8280, 1}, {8287, 16783875}, {8288, 0}, {8289, 2}, - {8292, 0}, {8293, 2}, {8304, 17035523}, {8305, 16779267}, - {8306, 2}, {8308, 16787715}, {8309, 17035779}, {8310, 17036035}, - {8311, 17036291}, {8312, 17036547}, {8313, 17036803}, {8314, 17037059}, - {8315, 17037315}, {8316, 17037571}, {8317, 17037827}, {8318, 17038083}, - {8319, 16780547}, {8320, 17035523}, {8321, 16786947}, {8322, 16785155}, - {8323, 16785411}, {8324, 16787715}, {8325, 17035779}, {8326, 17036035}, - {8327, 17036291}, {8328, 17036547}, {8329, 17036803}, {8330, 17037059}, - {8331, 17037315}, {8332, 17037571}, {8333, 17037827}, {8334, 17038083}, + {8292, 0}, {8293, 2}, {8304, 17035267}, {8305, 16779267}, + {8306, 2}, {8308, 16787715}, {8309, 17035523}, {8310, 17035779}, + {8311, 17036035}, {8312, 17036291}, {8313, 17036547}, {8314, 17036803}, + {8315, 17037059}, {8316, 17037315}, {8317, 17037571}, {8318, 17037827}, + {8319, 16780547}, {8320, 17035267}, {8321, 16786947}, {8322, 16785155}, + {8323, 16785411}, {8324, 16787715}, {8325, 17035523}, {8326, 17035779}, + {8327, 17036035}, {8328, 17036291}, {8329, 17036547}, {8330, 17036803}, + {8331, 17037059}, {8332, 17037315}, {8333, 17037571}, {8334, 17037827}, {8335, 2}, {8336, 16777219}, {8337, 16778243}, {8338, 16780803}, {8339, 16783107}, {8340, 16816387}, {8341, 16779011}, {8342, 16779779}, {8343, 16780035}, {8344, 16780291}, {8345, 16780547}, {8346, 16781059}, {8347, 16781827}, {8348, 16782083}, {8349, 2}, {8352, 1}, {8360, 33558787}, {8361, 1}, {8385, 2}, {8400, 1}, - {8433, 2}, {8448, 50592771}, {8449, 50593539}, {8450, 16777731}, - {8451, 33817091}, {8452, 1}, {8453, 50594819}, {8454, 50595587}, - {8455, 16816643}, {8456, 1}, {8457, 33819139}, {8458, 16778755}, + {8433, 2}, {8448, 50592515}, {8449, 50593283}, {8450, 16777731}, + {8451, 33816835}, {8452, 1}, {8453, 50594563}, {8454, 50595331}, + {8455, 16816643}, {8456, 1}, {8457, 33818883}, {8458, 16778755}, {8459, 16779011}, {8463, 16802051}, {8464, 16779267}, {8466, 16780035}, {8468, 1}, {8469, 16780547}, {8470, 33557763}, {8471, 1}, {8473, 16781059}, {8474, 16781315}, {8475, 16781571}, {8478, 1}, - {8480, 33819651}, {8481, 50597379}, {8482, 33820931}, {8483, 1}, + {8480, 33819395}, {8481, 50597123}, {8482, 33820675}, {8483, 1}, {8484, 16783619}, {8485, 1}, {8486, 16857091}, {8487, 1}, {8488, 16783619}, {8489, 1}, {8490, 16779779}, {8491, 16790787}, {8492, 16777475}, {8493, 16777731}, {8494, 1}, {8495, 16778243}, {8497, 16778499}, {8498, 2}, {8499, 16780291}, {8500, 16780803}, - {8501, 17044227}, {8502, 17044483}, {8503, 17044739}, {8504, 17044995}, - {8505, 16779267}, {8506, 1}, {8507, 50599683}, {8508, 16855043}, - {8509, 16852227}, {8511, 16855043}, {8512, 17046019}, {8513, 1}, + {8501, 17043971}, {8502, 17044227}, {8503, 17044483}, {8504, 17044739}, + {8505, 16779267}, {8506, 1}, {8507, 50599427}, {8508, 16855043}, + {8509, 16852227}, {8511, 16855043}, {8512, 17045763}, {8513, 1}, {8517, 16777987}, {8519, 16778243}, {8520, 16779267}, {8521, 16779523}, - {8522, 1}, {8528, 50600707}, {8529, 50601475}, {8530, 67379459}, - {8531, 50603267}, {8532, 50604035}, {8533, 50604803}, {8534, 50605571}, - {8535, 50606339}, {8536, 50607107}, {8537, 50607875}, {8538, 50608643}, - {8539, 50609411}, {8540, 50610179}, {8541, 50610947}, {8542, 50611715}, - {8543, 33564419}, {8544, 16779267}, {8545, 33835267}, {8546, 50612995}, - {8547, 33836547}, {8548, 16782595}, {8549, 33837059}, {8550, 50614787}, - {8551, 67392771}, {8552, 33839363}, {8553, 16783107}, {8554, 33839875}, - {8555, 50617603}, {8556, 16780035}, {8557, 16777731}, {8558, 16777987}, - {8559, 16780291}, {8560, 16779267}, {8561, 33835267}, {8562, 50612483}, - {8563, 33836547}, {8564, 16782595}, {8565, 33837059}, {8566, 50614787}, - {8567, 67392771}, {8568, 33839363}, {8569, 16783107}, {8570, 33839875}, - {8571, 50617603}, {8572, 16780035}, {8573, 16777731}, {8574, 16777987}, + {8522, 1}, {8528, 50600451}, {8529, 50601219}, {8530, 67379203}, + {8531, 50603011}, {8532, 50603779}, {8533, 50604547}, {8534, 50605315}, + {8535, 50606083}, {8536, 50606851}, {8537, 50607619}, {8538, 50608387}, + {8539, 50609155}, {8540, 50609923}, {8541, 50610691}, {8542, 50611459}, + {8543, 33564419}, {8544, 16779267}, {8545, 33835011}, {8546, 50612739}, + {8547, 33836291}, {8548, 16782595}, {8549, 33836803}, {8550, 50614531}, + {8551, 67392515}, {8552, 33839107}, {8553, 16783107}, {8554, 33839619}, + {8555, 50617347}, {8556, 16780035}, {8557, 16777731}, {8558, 16777987}, + {8559, 16780291}, {8560, 16779267}, {8561, 33835011}, {8562, 50612227}, + {8563, 33836291}, {8564, 16782595}, {8565, 33836803}, {8566, 50614531}, + {8567, 67392515}, {8568, 33839107}, {8569, 16783107}, {8570, 33839619}, + {8571, 50617347}, {8572, 16780035}, {8573, 16777731}, {8574, 16777987}, {8575, 16780291}, {8576, 1}, {8579, 2}, {8580, 1}, - {8585, 50618371}, {8586, 1}, {8588, 2}, {8592, 1}, - {8748, 33841923}, {8749, 50619651}, {8750, 1}, {8751, 33843203}, - {8752, 50620931}, {8753, 1}, {9001, 17067267}, {9002, 17067523}, + {8585, 50618115}, {8586, 1}, {8588, 2}, {8592, 1}, + {8748, 33841667}, {8749, 50619395}, {8750, 1}, {8751, 33842947}, + {8752, 50620675}, {8753, 1}, {9001, 17067011}, {9002, 17067267}, {9003, 1}, {9255, 2}, {9280, 1}, {9291, 2}, {9312, 16786947}, {9313, 16785155}, {9314, 16785411}, {9315, 16787715}, - {9316, 17035779}, {9317, 17036035}, {9318, 17036291}, {9319, 17036547}, - {9320, 17036803}, {9321, 33825539}, {9322, 33564163}, {9323, 33844995}, - {9324, 33845507}, {9325, 33846019}, {9326, 33846531}, {9327, 33847043}, - {9328, 33847555}, {9329, 33848067}, {9330, 33848579}, {9331, 33849091}, - {9332, 50626819}, {9333, 50627587}, {9334, 50628355}, {9335, 50629123}, - {9336, 50629891}, {9337, 50630659}, {9338, 50631427}, {9339, 50632195}, - {9340, 50632963}, {9341, 67410947}, {9342, 67411971}, {9343, 67412995}, - {9344, 67414019}, {9345, 67415043}, {9346, 67416067}, {9347, 67417091}, - {9348, 67418115}, {9349, 67419139}, {9350, 67420163}, {9351, 67421187}, - {9352, 2}, {9372, 50644995}, {9373, 50645763}, {9374, 50646531}, - {9375, 50647299}, {9376, 50648067}, {9377, 50648835}, {9378, 50649603}, - {9379, 50650371}, {9380, 50651139}, {9381, 50651907}, {9382, 50652675}, - {9383, 50653443}, {9384, 50654211}, {9385, 50654979}, {9386, 50655747}, - {9387, 50656515}, {9388, 50657283}, {9389, 50658051}, {9390, 50658819}, - {9391, 50659587}, {9392, 50660355}, {9393, 50661123}, {9394, 50661891}, - {9395, 50662659}, {9396, 50663427}, {9397, 50664195}, {9398, 16777219}, + {9316, 17035523}, {9317, 17035779}, {9318, 17036035}, {9319, 17036291}, + {9320, 17036547}, {9321, 33825283}, {9322, 33564163}, {9323, 33844739}, + {9324, 33845251}, {9325, 33845763}, {9326, 33846275}, {9327, 33846787}, + {9328, 33847299}, {9329, 33847811}, {9330, 33848323}, {9331, 33848835}, + {9332, 50626563}, {9333, 50627331}, {9334, 50628099}, {9335, 50628867}, + {9336, 50629635}, {9337, 50630403}, {9338, 50631171}, {9339, 50631939}, + {9340, 50632707}, {9341, 67410691}, {9342, 67411715}, {9343, 67412739}, + {9344, 67413763}, {9345, 67414787}, {9346, 67415811}, {9347, 67416835}, + {9348, 67417859}, {9349, 67418883}, {9350, 67419907}, {9351, 67420931}, + {9352, 2}, {9372, 50644739}, {9373, 50645507}, {9374, 50646275}, + {9375, 50647043}, {9376, 50647811}, {9377, 50648579}, {9378, 50649347}, + {9379, 50650115}, {9380, 50650883}, {9381, 50651651}, {9382, 50652419}, + {9383, 50653187}, {9384, 50653955}, {9385, 50654723}, {9386, 50655491}, + {9387, 50656259}, {9388, 50657027}, {9389, 50657795}, {9390, 50658563}, + {9391, 50659331}, {9392, 50660099}, {9393, 50660867}, {9394, 50661635}, + {9395, 50662403}, {9396, 50663171}, {9397, 50663939}, {9398, 16777219}, {9399, 16777475}, {9400, 16777731}, {9401, 16777987}, {9402, 16778243}, {9403, 16778499}, {9404, 16778755}, {9405, 16779011}, {9406, 16779267}, {9407, 16779523}, {9408, 16779779}, {9409, 16780035}, {9410, 16780291}, @@ -1284,343 +1285,343 @@ const uint32_t table[8000][2] = {9435, 16780035}, {9436, 16780291}, {9437, 16780547}, {9438, 16780803}, {9439, 16781059}, {9440, 16781315}, {9441, 16781571}, {9442, 16781827}, {9443, 16782083}, {9444, 16782339}, {9445, 16782595}, {9446, 16782851}, - {9447, 16783107}, {9448, 16783363}, {9449, 16783619}, {9450, 17035523}, - {9451, 1}, {10764, 67396355}, {10765, 1}, {10868, 50664963}, - {10869, 33888515}, {10870, 50665475}, {10871, 1}, {10972, 33889027}, + {9447, 16783107}, {9448, 16783363}, {9449, 16783619}, {9450, 17035267}, + {9451, 1}, {10764, 67396099}, {10765, 1}, {10868, 50664707}, + {10869, 33888259}, {10870, 50665219}, {10871, 1}, {10972, 33888771}, {10973, 1}, {11124, 2}, {11126, 1}, {11158, 2}, - {11159, 1}, {11264, 17112323}, {11265, 17112579}, {11266, 17112835}, - {11267, 17113091}, {11268, 17113347}, {11269, 17113603}, {11270, 17113859}, - {11271, 17114115}, {11272, 17114371}, {11273, 17114627}, {11274, 17114883}, - {11275, 17115139}, {11276, 17115395}, {11277, 17115651}, {11278, 17115907}, - {11279, 17116163}, {11280, 17116419}, {11281, 17116675}, {11282, 17116931}, - {11283, 17117187}, {11284, 17117443}, {11285, 17117699}, {11286, 17117955}, - {11287, 17118211}, {11288, 17118467}, {11289, 17118723}, {11290, 17118979}, - {11291, 17119235}, {11292, 17119491}, {11293, 17119747}, {11294, 17120003}, - {11295, 17120259}, {11296, 17120515}, {11297, 17120771}, {11298, 17121027}, - {11299, 17121283}, {11300, 17121539}, {11301, 17121795}, {11302, 17122051}, - {11303, 17122307}, {11304, 17122563}, {11305, 17122819}, {11306, 17123075}, - {11307, 17123331}, {11308, 17123587}, {11309, 17123843}, {11310, 17124099}, - {11311, 17124355}, {11312, 1}, {11360, 17124611}, {11361, 1}, - {11362, 17124867}, {11363, 17125123}, {11364, 17125379}, {11365, 1}, - {11367, 17125635}, {11368, 1}, {11369, 17125891}, {11370, 1}, - {11371, 17126147}, {11372, 1}, {11373, 16948483}, {11374, 16953091}, - {11375, 16948227}, {11376, 16950275}, {11377, 1}, {11378, 17126403}, - {11379, 1}, {11381, 17126659}, {11382, 1}, {11388, 16779523}, - {11389, 16782595}, {11390, 17126915}, {11391, 17127171}, {11392, 17127427}, - {11393, 1}, {11394, 17127683}, {11395, 1}, {11396, 17127939}, - {11397, 1}, {11398, 17128195}, {11399, 1}, {11400, 17128451}, - {11401, 1}, {11402, 17128707}, {11403, 1}, {11404, 17128963}, - {11405, 1}, {11406, 17129219}, {11407, 1}, {11408, 17129475}, - {11409, 1}, {11410, 17129731}, {11411, 1}, {11412, 17129987}, - {11413, 1}, {11414, 17130243}, {11415, 1}, {11416, 17130499}, - {11417, 1}, {11418, 17130755}, {11419, 1}, {11420, 17131011}, - {11421, 1}, {11422, 17131267}, {11423, 1}, {11424, 17131523}, - {11425, 1}, {11426, 17131779}, {11427, 1}, {11428, 17132035}, - {11429, 1}, {11430, 17132291}, {11431, 1}, {11432, 17132547}, - {11433, 1}, {11434, 17132803}, {11435, 1}, {11436, 17133059}, - {11437, 1}, {11438, 17133315}, {11439, 1}, {11440, 17133571}, - {11441, 1}, {11442, 17133827}, {11443, 1}, {11444, 17134083}, - {11445, 1}, {11446, 17134339}, {11447, 1}, {11448, 17134595}, - {11449, 1}, {11450, 17134851}, {11451, 1}, {11452, 17135107}, - {11453, 1}, {11454, 17135363}, {11455, 1}, {11456, 17135619}, - {11457, 1}, {11458, 17135875}, {11459, 1}, {11460, 17136131}, - {11461, 1}, {11462, 17136387}, {11463, 1}, {11464, 17136643}, - {11465, 1}, {11466, 17136899}, {11467, 1}, {11468, 17137155}, - {11469, 1}, {11470, 17137411}, {11471, 1}, {11472, 17137667}, - {11473, 1}, {11474, 17137923}, {11475, 1}, {11476, 17138179}, - {11477, 1}, {11478, 17138435}, {11479, 1}, {11480, 17138691}, - {11481, 1}, {11482, 17138947}, {11483, 1}, {11484, 17139203}, - {11485, 1}, {11486, 17139459}, {11487, 1}, {11488, 17139715}, - {11489, 1}, {11490, 17139971}, {11491, 1}, {11499, 17140227}, - {11500, 1}, {11501, 17140483}, {11502, 1}, {11506, 17140739}, + {11159, 1}, {11264, 17112067}, {11265, 17112323}, {11266, 17112579}, + {11267, 17112835}, {11268, 17113091}, {11269, 17113347}, {11270, 17113603}, + {11271, 17113859}, {11272, 17114115}, {11273, 17114371}, {11274, 17114627}, + {11275, 17114883}, {11276, 17115139}, {11277, 17115395}, {11278, 17115651}, + {11279, 17115907}, {11280, 17116163}, {11281, 17116419}, {11282, 17116675}, + {11283, 17116931}, {11284, 17117187}, {11285, 17117443}, {11286, 17117699}, + {11287, 17117955}, {11288, 17118211}, {11289, 17118467}, {11290, 17118723}, + {11291, 17118979}, {11292, 17119235}, {11293, 17119491}, {11294, 17119747}, + {11295, 17120003}, {11296, 17120259}, {11297, 17120515}, {11298, 17120771}, + {11299, 17121027}, {11300, 17121283}, {11301, 17121539}, {11302, 17121795}, + {11303, 17122051}, {11304, 17122307}, {11305, 17122563}, {11306, 17122819}, + {11307, 17123075}, {11308, 17123331}, {11309, 17123587}, {11310, 17123843}, + {11311, 17124099}, {11312, 1}, {11360, 17124355}, {11361, 1}, + {11362, 17124611}, {11363, 17124867}, {11364, 17125123}, {11365, 1}, + {11367, 17125379}, {11368, 1}, {11369, 17125635}, {11370, 1}, + {11371, 17125891}, {11372, 1}, {11373, 16948483}, {11374, 16953091}, + {11375, 16948227}, {11376, 16950275}, {11377, 1}, {11378, 17126147}, + {11379, 1}, {11381, 17126403}, {11382, 1}, {11388, 16779523}, + {11389, 16782595}, {11390, 17126659}, {11391, 17126915}, {11392, 17127171}, + {11393, 1}, {11394, 17127427}, {11395, 1}, {11396, 17127683}, + {11397, 1}, {11398, 17127939}, {11399, 1}, {11400, 17128195}, + {11401, 1}, {11402, 17128451}, {11403, 1}, {11404, 17128707}, + {11405, 1}, {11406, 17128963}, {11407, 1}, {11408, 17129219}, + {11409, 1}, {11410, 17129475}, {11411, 1}, {11412, 17129731}, + {11413, 1}, {11414, 17129987}, {11415, 1}, {11416, 17130243}, + {11417, 1}, {11418, 17130499}, {11419, 1}, {11420, 17130755}, + {11421, 1}, {11422, 17131011}, {11423, 1}, {11424, 17131267}, + {11425, 1}, {11426, 17131523}, {11427, 1}, {11428, 17131779}, + {11429, 1}, {11430, 17132035}, {11431, 1}, {11432, 17132291}, + {11433, 1}, {11434, 17132547}, {11435, 1}, {11436, 17132803}, + {11437, 1}, {11438, 17133059}, {11439, 1}, {11440, 17133315}, + {11441, 1}, {11442, 17133571}, {11443, 1}, {11444, 17133827}, + {11445, 1}, {11446, 17134083}, {11447, 1}, {11448, 17134339}, + {11449, 1}, {11450, 17134595}, {11451, 1}, {11452, 17134851}, + {11453, 1}, {11454, 17135107}, {11455, 1}, {11456, 17135363}, + {11457, 1}, {11458, 17135619}, {11459, 1}, {11460, 17135875}, + {11461, 1}, {11462, 17136131}, {11463, 1}, {11464, 17136387}, + {11465, 1}, {11466, 17136643}, {11467, 1}, {11468, 17136899}, + {11469, 1}, {11470, 17137155}, {11471, 1}, {11472, 17137411}, + {11473, 1}, {11474, 17137667}, {11475, 1}, {11476, 17137923}, + {11477, 1}, {11478, 17138179}, {11479, 1}, {11480, 17138435}, + {11481, 1}, {11482, 17138691}, {11483, 1}, {11484, 17138947}, + {11485, 1}, {11486, 17139203}, {11487, 1}, {11488, 17139459}, + {11489, 1}, {11490, 17139715}, {11491, 1}, {11499, 17139971}, + {11500, 1}, {11501, 17140227}, {11502, 1}, {11506, 17140483}, {11507, 1}, {11508, 2}, {11513, 1}, {11558, 2}, {11559, 1}, {11560, 2}, {11565, 1}, {11566, 2}, - {11568, 1}, {11624, 2}, {11631, 17140995}, {11632, 1}, + {11568, 1}, {11624, 2}, {11631, 17140739}, {11632, 1}, {11633, 2}, {11647, 1}, {11671, 2}, {11680, 1}, {11687, 2}, {11688, 1}, {11695, 2}, {11696, 1}, {11703, 2}, {11704, 1}, {11711, 2}, {11712, 1}, {11719, 2}, {11720, 1}, {11727, 2}, {11728, 1}, {11735, 2}, {11736, 1}, {11743, 2}, {11744, 1}, {11870, 2}, {11904, 1}, {11930, 2}, {11931, 1}, - {11935, 17141251}, {11936, 1}, {12019, 17141507}, {12020, 2}, - {12032, 17141763}, {12033, 17142019}, {12034, 17142275}, {12035, 17142531}, - {12036, 17142787}, {12037, 17143043}, {12038, 17143299}, {12039, 17143555}, - {12040, 17143811}, {12041, 17144067}, {12042, 17144323}, {12043, 17144579}, - {12044, 17144835}, {12045, 17145091}, {12046, 17145347}, {12047, 17145603}, - {12048, 17145859}, {12049, 17146115}, {12050, 17146371}, {12051, 17146627}, - {12052, 17146883}, {12053, 17147139}, {12054, 17147395}, {12055, 17147651}, - {12056, 17147907}, {12057, 17148163}, {12058, 17148419}, {12059, 17148675}, - {12060, 17148931}, {12061, 17149187}, {12062, 17149443}, {12063, 17149699}, - {12064, 17149955}, {12065, 17150211}, {12066, 17150467}, {12067, 17150723}, - {12068, 17150979}, {12069, 17151235}, {12070, 17151491}, {12071, 17151747}, - {12072, 17152003}, {12073, 17152259}, {12074, 17152515}, {12075, 17152771}, - {12076, 17153027}, {12077, 17153283}, {12078, 17153539}, {12079, 17153795}, - {12080, 17154051}, {12081, 17154307}, {12082, 17154563}, {12083, 17154819}, - {12084, 17155075}, {12085, 17155331}, {12086, 17155587}, {12087, 17155843}, - {12088, 17156099}, {12089, 17156355}, {12090, 17156611}, {12091, 17156867}, - {12092, 17157123}, {12093, 17157379}, {12094, 17157635}, {12095, 17157891}, - {12096, 17158147}, {12097, 17158403}, {12098, 17158659}, {12099, 17158915}, - {12100, 17159171}, {12101, 17159427}, {12102, 17159683}, {12103, 17159939}, - {12104, 17160195}, {12105, 17160451}, {12106, 17160707}, {12107, 17160963}, - {12108, 17161219}, {12109, 17161475}, {12110, 17161731}, {12111, 17161987}, - {12112, 17162243}, {12113, 17162499}, {12114, 17162755}, {12115, 17163011}, - {12116, 17163267}, {12117, 17163523}, {12118, 17163779}, {12119, 17164035}, - {12120, 17164291}, {12121, 17164547}, {12122, 17164803}, {12123, 17165059}, - {12124, 17165315}, {12125, 17165571}, {12126, 17165827}, {12127, 17166083}, - {12128, 17166339}, {12129, 17166595}, {12130, 17166851}, {12131, 17167107}, - {12132, 17167363}, {12133, 17167619}, {12134, 17167875}, {12135, 17168131}, - {12136, 17168387}, {12137, 17168643}, {12138, 17168899}, {12139, 17169155}, - {12140, 17169411}, {12141, 17169667}, {12142, 17169923}, {12143, 17170179}, - {12144, 17170435}, {12145, 17170691}, {12146, 17170947}, {12147, 17171203}, - {12148, 17171459}, {12149, 17171715}, {12150, 17171971}, {12151, 17172227}, - {12152, 17172483}, {12153, 17172739}, {12154, 17172995}, {12155, 17173251}, - {12156, 17173507}, {12157, 17173763}, {12158, 17174019}, {12159, 17174275}, - {12160, 17174531}, {12161, 17174787}, {12162, 17175043}, {12163, 17175299}, - {12164, 17175555}, {12165, 17175811}, {12166, 17176067}, {12167, 17176323}, - {12168, 17176579}, {12169, 17176835}, {12170, 17177091}, {12171, 17177347}, - {12172, 17177603}, {12173, 17177859}, {12174, 17178115}, {12175, 17178371}, - {12176, 17178627}, {12177, 17178883}, {12178, 17179139}, {12179, 17179395}, - {12180, 17179651}, {12181, 17179907}, {12182, 17180163}, {12183, 17180419}, - {12184, 17180675}, {12185, 17180931}, {12186, 17181187}, {12187, 17181443}, - {12188, 17181699}, {12189, 17181955}, {12190, 17182211}, {12191, 17182467}, - {12192, 17182723}, {12193, 17182979}, {12194, 17183235}, {12195, 17183491}, - {12196, 17183747}, {12197, 17184003}, {12198, 17184259}, {12199, 17184515}, - {12200, 17184771}, {12201, 17185027}, {12202, 17185283}, {12203, 17185539}, - {12204, 17185795}, {12205, 17186051}, {12206, 17186307}, {12207, 17186563}, - {12208, 17186819}, {12209, 17187075}, {12210, 17187331}, {12211, 17187587}, - {12212, 17187843}, {12213, 17188099}, {12214, 17188355}, {12215, 17188611}, - {12216, 17188867}, {12217, 17189123}, {12218, 17189379}, {12219, 17189635}, - {12220, 17189891}, {12221, 17190147}, {12222, 17190403}, {12223, 17190659}, - {12224, 17190915}, {12225, 17191171}, {12226, 17191427}, {12227, 17191683}, - {12228, 17191939}, {12229, 17192195}, {12230, 17192451}, {12231, 17192707}, - {12232, 17192963}, {12233, 17193219}, {12234, 17193475}, {12235, 17193731}, - {12236, 17193987}, {12237, 17194243}, {12238, 17194499}, {12239, 17194755}, - {12240, 17195011}, {12241, 17195267}, {12242, 17195523}, {12243, 17195779}, - {12244, 17196035}, {12245, 17196291}, {12246, 2}, {12288, 16783875}, - {12289, 1}, {12290, 17196547}, {12291, 1}, {12342, 17196803}, - {12343, 1}, {12344, 17147651}, {12345, 17197059}, {12346, 17197315}, + {11935, 17140995}, {11936, 1}, {12019, 17141251}, {12020, 2}, + {12032, 17141507}, {12033, 17141763}, {12034, 17142019}, {12035, 17142275}, + {12036, 17142531}, {12037, 17142787}, {12038, 17143043}, {12039, 17143299}, + {12040, 17143555}, {12041, 17143811}, {12042, 17144067}, {12043, 17144323}, + {12044, 17144579}, {12045, 17144835}, {12046, 17145091}, {12047, 17145347}, + {12048, 17145603}, {12049, 17145859}, {12050, 17146115}, {12051, 17146371}, + {12052, 17146627}, {12053, 17146883}, {12054, 17147139}, {12055, 17147395}, + {12056, 17147651}, {12057, 17147907}, {12058, 17148163}, {12059, 17148419}, + {12060, 17148675}, {12061, 17148931}, {12062, 17149187}, {12063, 17149443}, + {12064, 17149699}, {12065, 17149955}, {12066, 17150211}, {12067, 17150467}, + {12068, 17150723}, {12069, 17150979}, {12070, 17151235}, {12071, 17151491}, + {12072, 17151747}, {12073, 17152003}, {12074, 17152259}, {12075, 17152515}, + {12076, 17152771}, {12077, 17153027}, {12078, 17153283}, {12079, 17153539}, + {12080, 17153795}, {12081, 17154051}, {12082, 17154307}, {12083, 17154563}, + {12084, 17154819}, {12085, 17155075}, {12086, 17155331}, {12087, 17155587}, + {12088, 17155843}, {12089, 17156099}, {12090, 17156355}, {12091, 17156611}, + {12092, 17156867}, {12093, 17157123}, {12094, 17157379}, {12095, 17157635}, + {12096, 17157891}, {12097, 17158147}, {12098, 17158403}, {12099, 17158659}, + {12100, 17158915}, {12101, 17159171}, {12102, 17159427}, {12103, 17159683}, + {12104, 17159939}, {12105, 17160195}, {12106, 17160451}, {12107, 17160707}, + {12108, 17160963}, {12109, 17161219}, {12110, 17161475}, {12111, 17161731}, + {12112, 17161987}, {12113, 17162243}, {12114, 17162499}, {12115, 17162755}, + {12116, 17163011}, {12117, 17163267}, {12118, 17163523}, {12119, 17163779}, + {12120, 17164035}, {12121, 17164291}, {12122, 17164547}, {12123, 17164803}, + {12124, 17165059}, {12125, 17165315}, {12126, 17165571}, {12127, 17165827}, + {12128, 17166083}, {12129, 17166339}, {12130, 17166595}, {12131, 17166851}, + {12132, 17167107}, {12133, 17167363}, {12134, 17167619}, {12135, 17167875}, + {12136, 17168131}, {12137, 17168387}, {12138, 17168643}, {12139, 17168899}, + {12140, 17169155}, {12141, 17169411}, {12142, 17169667}, {12143, 17169923}, + {12144, 17170179}, {12145, 17170435}, {12146, 17170691}, {12147, 17170947}, + {12148, 17171203}, {12149, 17171459}, {12150, 17171715}, {12151, 17171971}, + {12152, 17172227}, {12153, 17172483}, {12154, 17172739}, {12155, 17172995}, + {12156, 17173251}, {12157, 17173507}, {12158, 17173763}, {12159, 17174019}, + {12160, 17174275}, {12161, 17174531}, {12162, 17174787}, {12163, 17175043}, + {12164, 17175299}, {12165, 17175555}, {12166, 17175811}, {12167, 17176067}, + {12168, 17176323}, {12169, 17176579}, {12170, 17176835}, {12171, 17177091}, + {12172, 17177347}, {12173, 17177603}, {12174, 17177859}, {12175, 17178115}, + {12176, 17178371}, {12177, 17178627}, {12178, 17178883}, {12179, 17179139}, + {12180, 17179395}, {12181, 17179651}, {12182, 17179907}, {12183, 17180163}, + {12184, 17180419}, {12185, 17180675}, {12186, 17180931}, {12187, 17181187}, + {12188, 17181443}, {12189, 17181699}, {12190, 17181955}, {12191, 17182211}, + {12192, 17182467}, {12193, 17182723}, {12194, 17182979}, {12195, 17183235}, + {12196, 17183491}, {12197, 17183747}, {12198, 17184003}, {12199, 17184259}, + {12200, 17184515}, {12201, 17184771}, {12202, 17185027}, {12203, 17185283}, + {12204, 17185539}, {12205, 17185795}, {12206, 17186051}, {12207, 17186307}, + {12208, 17186563}, {12209, 17186819}, {12210, 17187075}, {12211, 17187331}, + {12212, 17187587}, {12213, 17187843}, {12214, 17188099}, {12215, 17188355}, + {12216, 17188611}, {12217, 17188867}, {12218, 17189123}, {12219, 17189379}, + {12220, 17189635}, {12221, 17189891}, {12222, 17190147}, {12223, 17190403}, + {12224, 17190659}, {12225, 17190915}, {12226, 17191171}, {12227, 17191427}, + {12228, 17191683}, {12229, 17191939}, {12230, 17192195}, {12231, 17192451}, + {12232, 17192707}, {12233, 17192963}, {12234, 17193219}, {12235, 17193475}, + {12236, 17193731}, {12237, 17193987}, {12238, 17194243}, {12239, 17194499}, + {12240, 17194755}, {12241, 17195011}, {12242, 17195267}, {12243, 17195523}, + {12244, 17195779}, {12245, 17196035}, {12246, 2}, {12288, 16783875}, + {12289, 1}, {12290, 17196291}, {12291, 1}, {12342, 17196547}, + {12343, 1}, {12344, 17147395}, {12345, 17196803}, {12346, 17197059}, {12347, 1}, {12352, 2}, {12353, 1}, {12439, 2}, - {12441, 1}, {12443, 33974787}, {12444, 33975299}, {12445, 1}, - {12447, 33975811}, {12448, 1}, {12543, 33976323}, {12544, 2}, - {12549, 1}, {12592, 2}, {12593, 17199619}, {12594, 17199875}, - {12595, 17200131}, {12596, 17200387}, {12597, 17200643}, {12598, 17200899}, - {12599, 17201155}, {12600, 17201411}, {12601, 17201667}, {12602, 17201923}, - {12603, 17202179}, {12604, 17202435}, {12605, 17202691}, {12606, 17202947}, - {12607, 17203203}, {12608, 17203459}, {12609, 17203715}, {12610, 17203971}, - {12611, 17204227}, {12612, 17204483}, {12613, 17204739}, {12614, 17204995}, - {12615, 17205251}, {12616, 17205507}, {12617, 17205763}, {12618, 17206019}, - {12619, 17206275}, {12620, 17206531}, {12621, 17206787}, {12622, 17207043}, - {12623, 17207299}, {12624, 17207555}, {12625, 17207811}, {12626, 17208067}, - {12627, 17208323}, {12628, 17208579}, {12629, 17208835}, {12630, 17209091}, - {12631, 17209347}, {12632, 17209603}, {12633, 17209859}, {12634, 17210115}, - {12635, 17210371}, {12636, 17210627}, {12637, 17210883}, {12638, 17211139}, - {12639, 17211395}, {12640, 17211651}, {12641, 17211907}, {12642, 17212163}, - {12643, 17212419}, {12644, 2}, {12645, 17212675}, {12646, 17212931}, - {12647, 17213187}, {12648, 17213443}, {12649, 17213699}, {12650, 17213955}, - {12651, 17214211}, {12652, 17214467}, {12653, 17214723}, {12654, 17214979}, - {12655, 17215235}, {12656, 17215491}, {12657, 17215747}, {12658, 17216003}, - {12659, 17216259}, {12660, 17216515}, {12661, 17216771}, {12662, 17217027}, - {12663, 17217283}, {12664, 17217539}, {12665, 17217795}, {12666, 17218051}, - {12667, 17218307}, {12668, 17218563}, {12669, 17218819}, {12670, 17219075}, - {12671, 17219331}, {12672, 17219587}, {12673, 17219843}, {12674, 17220099}, - {12675, 17220355}, {12676, 17220611}, {12677, 17220867}, {12678, 17221123}, - {12679, 17221379}, {12680, 17221635}, {12681, 17221891}, {12682, 17222147}, - {12683, 17222403}, {12684, 17222659}, {12685, 17222915}, {12686, 17223171}, - {12687, 2}, {12688, 1}, {12690, 17141763}, {12691, 17143299}, - {12692, 17223427}, {12693, 17223683}, {12694, 17223939}, {12695, 17224195}, - {12696, 17224451}, {12697, 17224707}, {12698, 17142787}, {12699, 17224963}, - {12700, 17225219}, {12701, 17225475}, {12702, 17225731}, {12703, 17143811}, - {12704, 1}, {12772, 2}, {12784, 1}, {12800, 50780419}, - {12801, 50781187}, {12802, 50781955}, {12803, 50782723}, {12804, 50783491}, - {12805, 50784259}, {12806, 50785027}, {12807, 50785795}, {12808, 50786563}, - {12809, 50787331}, {12810, 50788099}, {12811, 50788867}, {12812, 50789635}, - {12813, 50790403}, {12814, 50791171}, {12815, 50791939}, {12816, 50792707}, - {12817, 50793475}, {12818, 50794243}, {12819, 50795011}, {12820, 50795779}, - {12821, 50796547}, {12822, 50797315}, {12823, 50798083}, {12824, 50798851}, - {12825, 50799619}, {12826, 50800387}, {12827, 50801155}, {12828, 50801923}, - {12829, 67579907}, {12830, 67580931}, {12831, 2}, {12832, 50804739}, - {12833, 50805507}, {12834, 50806275}, {12835, 50807043}, {12836, 50807811}, - {12837, 50808579}, {12838, 50809347}, {12839, 50810115}, {12840, 50810883}, - {12841, 50811651}, {12842, 50812419}, {12843, 50813187}, {12844, 50813955}, - {12845, 50814723}, {12846, 50815491}, {12847, 50816259}, {12848, 50817027}, - {12849, 50817795}, {12850, 50818563}, {12851, 50819331}, {12852, 50820099}, - {12853, 50820867}, {12854, 50821635}, {12855, 50822403}, {12856, 50823171}, - {12857, 50823939}, {12858, 50824707}, {12859, 50825475}, {12860, 50826243}, - {12861, 50827011}, {12862, 50827779}, {12863, 50828547}, {12864, 50829315}, - {12865, 50830083}, {12866, 50830851}, {12867, 50831619}, {12868, 17277955}, - {12869, 17278211}, {12870, 17158659}, {12871, 17278467}, {12872, 1}, - {12880, 50833155}, {12881, 33845251}, {12882, 34056707}, {12883, 33562371}, - {12884, 34057219}, {12885, 34057731}, {12886, 34058243}, {12887, 34058755}, - {12888, 34059267}, {12889, 34059779}, {12890, 34060291}, {12891, 33827331}, - {12892, 33826563}, {12893, 34060803}, {12894, 34061315}, {12895, 34061827}, - {12896, 17199619}, {12897, 17200387}, {12898, 17201155}, {12899, 17201667}, - {12900, 17203715}, {12901, 17203971}, {12902, 17204739}, {12903, 17205251}, - {12904, 17205507}, {12905, 17206019}, {12906, 17206275}, {12907, 17206531}, - {12908, 17206787}, {12909, 17207043}, {12910, 17236995}, {12911, 17237763}, - {12912, 17238531}, {12913, 17239299}, {12914, 17240067}, {12915, 17240835}, - {12916, 17241603}, {12917, 17242371}, {12918, 17243139}, {12919, 17243907}, - {12920, 17244675}, {12921, 17245443}, {12922, 17246211}, {12923, 17246979}, - {12924, 34062339}, {12925, 34062851}, {12926, 17286147}, {12927, 1}, - {12928, 17141763}, {12929, 17143299}, {12930, 17223427}, {12931, 17223683}, - {12932, 17253635}, {12933, 17254403}, {12934, 17255171}, {12935, 17144579}, - {12936, 17256707}, {12937, 17147651}, {12938, 17160451}, {12939, 17163523}, - {12940, 17163267}, {12941, 17160707}, {12942, 17184259}, {12943, 17149699}, - {12944, 17159939}, {12945, 17263619}, {12946, 17264387}, {12947, 17265155}, - {12948, 17265923}, {12949, 17266691}, {12950, 17267459}, {12951, 17268227}, - {12952, 17268995}, {12953, 17286403}, {12954, 17286659}, {12955, 17151235}, - {12956, 17286915}, {12957, 17287171}, {12958, 17287427}, {12959, 17287683}, - {12960, 17287939}, {12961, 17275907}, {12962, 17288195}, {12963, 17288451}, - {12964, 17223939}, {12965, 17224195}, {12966, 17224451}, {12967, 17288707}, - {12968, 17288963}, {12969, 17289219}, {12970, 17289475}, {12971, 17271299}, - {12972, 17272067}, {12973, 17272835}, {12974, 17273603}, {12975, 17274371}, - {12976, 17289731}, {12977, 34067203}, {12978, 34067715}, {12979, 34068227}, - {12980, 34068739}, {12981, 34069251}, {12982, 33564931}, {12983, 34057475}, - {12984, 34061571}, {12985, 34069763}, {12986, 34070275}, {12987, 34070787}, - {12988, 34071299}, {12989, 34071811}, {12990, 34072323}, {12991, 34072835}, - {12992, 34073347}, {12993, 34073859}, {12994, 34074371}, {12995, 34074883}, - {12996, 34075395}, {12997, 34075907}, {12998, 34076419}, {12999, 34076931}, - {13000, 34077443}, {13001, 50855171}, {13002, 50855939}, {13003, 50856707}, - {13004, 34080259}, {13005, 50857987}, {13006, 34081539}, {13007, 50859267}, - {13008, 17305603}, {13009, 17305859}, {13010, 17306115}, {13011, 17306371}, - {13012, 17306627}, {13013, 17306883}, {13014, 17307139}, {13015, 17307395}, - {13016, 17307651}, {13017, 17199107}, {13018, 17307907}, {13019, 17308163}, - {13020, 17308419}, {13021, 17308675}, {13022, 17308931}, {13023, 17309187}, - {13024, 17309443}, {13025, 17309699}, {13026, 17309955}, {13027, 17199363}, - {13028, 17310211}, {13029, 17310467}, {13030, 17310723}, {13031, 17310979}, - {13032, 17311235}, {13033, 17311491}, {13034, 17311747}, {13035, 17312003}, - {13036, 17312259}, {13037, 17312515}, {13038, 17312771}, {13039, 17313027}, - {13040, 17313283}, {13041, 17313539}, {13042, 17313795}, {13043, 17314051}, - {13044, 17314307}, {13045, 17314563}, {13046, 17314819}, {13047, 17315075}, - {13048, 17315331}, {13049, 17315587}, {13050, 17315843}, {13051, 17316099}, - {13052, 17316355}, {13053, 17316611}, {13054, 17316867}, {13055, 34094339}, - {13056, 67649283}, {13057, 67650307}, {13058, 67651331}, {13059, 50875139}, - {13060, 67653123}, {13061, 50876931}, {13062, 50877699}, {13063, 84432899}, - {13064, 67656963}, {13065, 50880771}, {13066, 50881539}, {13067, 50882307}, - {13068, 67660291}, {13069, 67661315}, {13070, 50885123}, {13071, 50885891}, - {13072, 34109443}, {13073, 50887171}, {13074, 67665155}, {13075, 67666179}, - {13076, 34112771}, {13077, 84444931}, {13078, 101223427}, {13079, 84447747}, - {13080, 50891011}, {13081, 84449027}, {13082, 84450307}, {13083, 67674371}, - {13084, 50898179}, {13085, 50898947}, {13086, 50899715}, {13087, 67677699}, - {13088, 84455939}, {13089, 67680003}, {13090, 50903811}, {13091, 50904579}, - {13092, 50905347}, {13093, 34128899}, {13094, 34129411}, {13095, 34118147}, - {13096, 34129923}, {13097, 50907651}, {13098, 50908419}, {13099, 84463619}, - {13100, 50910467}, {13101, 67688451}, {13102, 84466691}, {13103, 50913539}, - {13104, 34137091}, {13105, 34137603}, {13106, 84469763}, {13107, 67693827}, - {13108, 84472067}, {13109, 50918915}, {13110, 84474115}, {13111, 34143747}, - {13112, 50921475}, {13113, 50922243}, {13114, 50923011}, {13115, 50923779}, - {13116, 50924547}, {13117, 67702531}, {13118, 50926339}, {13119, 34149891}, - {13120, 50927619}, {13121, 50928387}, {13122, 50929155}, {13123, 67707139}, - {13124, 50930947}, {13125, 50931715}, {13126, 50932483}, {13127, 84487683}, - {13128, 67711747}, {13129, 34158339}, {13130, 84490499}, {13131, 34160131}, - {13132, 67715075}, {13133, 67669507}, {13134, 50938883}, {13135, 50939651}, - {13136, 50940419}, {13137, 67718403}, {13138, 34164995}, {13139, 50942723}, - {13140, 67720707}, {13141, 34167299}, {13142, 84499459}, {13143, 50893827}, - {13144, 34169091}, {13145, 34169603}, {13146, 34170115}, {13147, 34170627}, - {13148, 34171139}, {13149, 34171651}, {13150, 34172163}, {13151, 34172675}, - {13152, 34173187}, {13153, 34173699}, {13154, 50951427}, {13155, 50952195}, - {13156, 50952963}, {13157, 50953731}, {13158, 50954499}, {13159, 50955267}, - {13160, 50956035}, {13161, 50956803}, {13162, 50957571}, {13163, 50958339}, - {13164, 50959107}, {13165, 50959875}, {13166, 50960643}, {13167, 50961411}, - {13168, 50962179}, {13169, 50962947}, {13170, 34186499}, {13171, 34187011}, - {13172, 50964739}, {13173, 34188291}, {13174, 34188803}, {13175, 34189315}, - {13176, 50967043}, {13177, 50967811}, {13178, 34191363}, {13179, 34191875}, - {13180, 34192387}, {13181, 34192899}, {13182, 34193411}, {13183, 67748355}, - {13184, 34185987}, {13185, 34194947}, {13186, 34195459}, {13187, 34195971}, - {13188, 34196483}, {13189, 34196995}, {13190, 34197507}, {13191, 34198019}, - {13192, 50975747}, {13193, 67753731}, {13194, 34200323}, {13195, 34200835}, - {13196, 34201347}, {13197, 34201859}, {13198, 34202371}, {13199, 34202883}, - {13200, 34203395}, {13201, 50981123}, {13202, 50981891}, {13203, 50980355}, - {13204, 50982659}, {13205, 34206211}, {13206, 34206723}, {13207, 34207235}, - {13208, 33556995}, {13209, 34207747}, {13210, 34208259}, {13211, 34208771}, - {13212, 34209283}, {13213, 34209795}, {13214, 34210307}, {13215, 50988035}, - {13216, 50988803}, {13217, 34190083}, {13218, 50989571}, {13219, 50990339}, - {13220, 50991107}, {13221, 34190851}, {13222, 50991875}, {13223, 50992643}, - {13224, 67770627}, {13225, 34185987}, {13226, 50994435}, {13227, 50995203}, - {13228, 50995971}, {13229, 50996739}, {13230, 84551939}, {13231, 101330435}, - {13232, 34223107}, {13233, 34223619}, {13234, 34224131}, {13235, 34224643}, - {13236, 34225155}, {13237, 34225667}, {13238, 34226179}, {13239, 34226691}, - {13240, 34227203}, {13241, 34226691}, {13242, 34227715}, {13243, 34228227}, - {13244, 34228739}, {13245, 34229251}, {13246, 34229763}, {13247, 34229251}, - {13248, 34230275}, {13249, 34230787}, {13250, 2}, {13251, 34231299}, - {13252, 33817347}, {13253, 33554947}, {13254, 67786243}, {13255, 2}, - {13256, 34232835}, {13257, 34233347}, {13258, 34233859}, {13259, 34185731}, - {13260, 34234371}, {13261, 34234883}, {13262, 34210307}, {13263, 34235395}, - {13264, 33557251}, {13265, 34235907}, {13266, 51013635}, {13267, 34237187}, - {13268, 34197507}, {13269, 51014915}, {13270, 51015683}, {13271, 34239235}, - {13272, 2}, {13273, 51016963}, {13274, 34240515}, {13275, 34221315}, - {13276, 34241027}, {13277, 34241539}, {13278, 51019267}, {13279, 51020035}, - {13280, 34243587}, {13281, 34244099}, {13282, 34244611}, {13283, 34245123}, - {13284, 34245635}, {13285, 34246147}, {13286, 34246659}, {13287, 34247171}, - {13288, 34247683}, {13289, 51025411}, {13290, 51026179}, {13291, 51026947}, - {13292, 51027715}, {13293, 51028483}, {13294, 51029251}, {13295, 51030019}, - {13296, 51030787}, {13297, 51031555}, {13298, 51032323}, {13299, 51033091}, - {13300, 51033859}, {13301, 51034627}, {13302, 51035395}, {13303, 51036163}, - {13304, 51036931}, {13305, 51037699}, {13306, 51038467}, {13307, 51039235}, - {13308, 51040003}, {13309, 51040771}, {13310, 51041539}, {13311, 51042307}, + {12441, 1}, {12443, 33974531}, {12444, 33975043}, {12445, 1}, + {12447, 33975555}, {12448, 1}, {12543, 33976067}, {12544, 2}, + {12549, 1}, {12592, 2}, {12593, 17199363}, {12594, 17199619}, + {12595, 17199875}, {12596, 17200131}, {12597, 17200387}, {12598, 17200643}, + {12599, 17200899}, {12600, 17201155}, {12601, 17201411}, {12602, 17201667}, + {12603, 17201923}, {12604, 17202179}, {12605, 17202435}, {12606, 17202691}, + {12607, 17202947}, {12608, 17203203}, {12609, 17203459}, {12610, 17203715}, + {12611, 17203971}, {12612, 17204227}, {12613, 17204483}, {12614, 17204739}, + {12615, 17204995}, {12616, 17205251}, {12617, 17205507}, {12618, 17205763}, + {12619, 17206019}, {12620, 17206275}, {12621, 17206531}, {12622, 17206787}, + {12623, 17207043}, {12624, 17207299}, {12625, 17207555}, {12626, 17207811}, + {12627, 17208067}, {12628, 17208323}, {12629, 17208579}, {12630, 17208835}, + {12631, 17209091}, {12632, 17209347}, {12633, 17209603}, {12634, 17209859}, + {12635, 17210115}, {12636, 17210371}, {12637, 17210627}, {12638, 17210883}, + {12639, 17211139}, {12640, 17211395}, {12641, 17211651}, {12642, 17211907}, + {12643, 17212163}, {12644, 2}, {12645, 17212419}, {12646, 17212675}, + {12647, 17212931}, {12648, 17213187}, {12649, 17213443}, {12650, 17213699}, + {12651, 17213955}, {12652, 17214211}, {12653, 17214467}, {12654, 17214723}, + {12655, 17214979}, {12656, 17215235}, {12657, 17215491}, {12658, 17215747}, + {12659, 17216003}, {12660, 17216259}, {12661, 17216515}, {12662, 17216771}, + {12663, 17217027}, {12664, 17217283}, {12665, 17217539}, {12666, 17217795}, + {12667, 17218051}, {12668, 17218307}, {12669, 17218563}, {12670, 17218819}, + {12671, 17219075}, {12672, 17219331}, {12673, 17219587}, {12674, 17219843}, + {12675, 17220099}, {12676, 17220355}, {12677, 17220611}, {12678, 17220867}, + {12679, 17221123}, {12680, 17221379}, {12681, 17221635}, {12682, 17221891}, + {12683, 17222147}, {12684, 17222403}, {12685, 17222659}, {12686, 17222915}, + {12687, 2}, {12688, 1}, {12690, 17141507}, {12691, 17143043}, + {12692, 17223171}, {12693, 17223427}, {12694, 17223683}, {12695, 17223939}, + {12696, 17224195}, {12697, 17224451}, {12698, 17142531}, {12699, 17224707}, + {12700, 17224963}, {12701, 17225219}, {12702, 17225475}, {12703, 17143555}, + {12704, 1}, {12772, 2}, {12784, 1}, {12800, 50780163}, + {12801, 50780931}, {12802, 50781699}, {12803, 50782467}, {12804, 50783235}, + {12805, 50784003}, {12806, 50784771}, {12807, 50785539}, {12808, 50786307}, + {12809, 50787075}, {12810, 50787843}, {12811, 50788611}, {12812, 50789379}, + {12813, 50790147}, {12814, 50790915}, {12815, 50791683}, {12816, 50792451}, + {12817, 50793219}, {12818, 50793987}, {12819, 50794755}, {12820, 50795523}, + {12821, 50796291}, {12822, 50797059}, {12823, 50797827}, {12824, 50798595}, + {12825, 50799363}, {12826, 50800131}, {12827, 50800899}, {12828, 50801667}, + {12829, 67579651}, {12830, 67580675}, {12831, 2}, {12832, 50804483}, + {12833, 50805251}, {12834, 50806019}, {12835, 50806787}, {12836, 50807555}, + {12837, 50808323}, {12838, 50809091}, {12839, 50809859}, {12840, 50810627}, + {12841, 50811395}, {12842, 50812163}, {12843, 50812931}, {12844, 50813699}, + {12845, 50814467}, {12846, 50815235}, {12847, 50816003}, {12848, 50816771}, + {12849, 50817539}, {12850, 50818307}, {12851, 50819075}, {12852, 50819843}, + {12853, 50820611}, {12854, 50821379}, {12855, 50822147}, {12856, 50822915}, + {12857, 50823683}, {12858, 50824451}, {12859, 50825219}, {12860, 50825987}, + {12861, 50826755}, {12862, 50827523}, {12863, 50828291}, {12864, 50829059}, + {12865, 50829827}, {12866, 50830595}, {12867, 50831363}, {12868, 17277699}, + {12869, 17277955}, {12870, 17158403}, {12871, 17278211}, {12872, 1}, + {12880, 50832899}, {12881, 33844995}, {12882, 34056451}, {12883, 33562371}, + {12884, 34056963}, {12885, 34057475}, {12886, 34057987}, {12887, 34058499}, + {12888, 34059011}, {12889, 34059523}, {12890, 34060035}, {12891, 33827075}, + {12892, 33826307}, {12893, 34060547}, {12894, 34061059}, {12895, 34061571}, + {12896, 17199363}, {12897, 17200131}, {12898, 17200899}, {12899, 17201411}, + {12900, 17203459}, {12901, 17203715}, {12902, 17204483}, {12903, 17204995}, + {12904, 17205251}, {12905, 17205763}, {12906, 17206019}, {12907, 17206275}, + {12908, 17206531}, {12909, 17206787}, {12910, 17236739}, {12911, 17237507}, + {12912, 17238275}, {12913, 17239043}, {12914, 17239811}, {12915, 17240579}, + {12916, 17241347}, {12917, 17242115}, {12918, 17242883}, {12919, 17243651}, + {12920, 17244419}, {12921, 17245187}, {12922, 17245955}, {12923, 17246723}, + {12924, 34062083}, {12925, 34062595}, {12926, 17285891}, {12927, 1}, + {12928, 17141507}, {12929, 17143043}, {12930, 17223171}, {12931, 17223427}, + {12932, 17253379}, {12933, 17254147}, {12934, 17254915}, {12935, 17144323}, + {12936, 17256451}, {12937, 17147395}, {12938, 17160195}, {12939, 17163267}, + {12940, 17163011}, {12941, 17160451}, {12942, 17184003}, {12943, 17149443}, + {12944, 17159683}, {12945, 17263363}, {12946, 17264131}, {12947, 17264899}, + {12948, 17265667}, {12949, 17266435}, {12950, 17267203}, {12951, 17267971}, + {12952, 17268739}, {12953, 17286147}, {12954, 17286403}, {12955, 17150979}, + {12956, 17286659}, {12957, 17286915}, {12958, 17287171}, {12959, 17287427}, + {12960, 17287683}, {12961, 17275651}, {12962, 17287939}, {12963, 17288195}, + {12964, 17223683}, {12965, 17223939}, {12966, 17224195}, {12967, 17288451}, + {12968, 17288707}, {12969, 17288963}, {12970, 17289219}, {12971, 17271043}, + {12972, 17271811}, {12973, 17272579}, {12974, 17273347}, {12975, 17274115}, + {12976, 17289475}, {12977, 34066947}, {12978, 34067459}, {12979, 34067971}, + {12980, 34068483}, {12981, 34068995}, {12982, 33564931}, {12983, 34057219}, + {12984, 34061315}, {12985, 34069507}, {12986, 34070019}, {12987, 34070531}, + {12988, 34071043}, {12989, 34071555}, {12990, 34072067}, {12991, 34072579}, + {12992, 34073091}, {12993, 34073603}, {12994, 34074115}, {12995, 34074627}, + {12996, 34075139}, {12997, 34075651}, {12998, 34076163}, {12999, 34076675}, + {13000, 34077187}, {13001, 50854915}, {13002, 50855683}, {13003, 50856451}, + {13004, 34080003}, {13005, 50857731}, {13006, 34081283}, {13007, 50859011}, + {13008, 17305347}, {13009, 17305603}, {13010, 17305859}, {13011, 17306115}, + {13012, 17306371}, {13013, 17306627}, {13014, 17306883}, {13015, 17307139}, + {13016, 17307395}, {13017, 17198851}, {13018, 17307651}, {13019, 17307907}, + {13020, 17308163}, {13021, 17308419}, {13022, 17308675}, {13023, 17308931}, + {13024, 17309187}, {13025, 17309443}, {13026, 17309699}, {13027, 17199107}, + {13028, 17309955}, {13029, 17310211}, {13030, 17310467}, {13031, 17310723}, + {13032, 17310979}, {13033, 17311235}, {13034, 17311491}, {13035, 17311747}, + {13036, 17312003}, {13037, 17312259}, {13038, 17312515}, {13039, 17312771}, + {13040, 17313027}, {13041, 17313283}, {13042, 17313539}, {13043, 17313795}, + {13044, 17314051}, {13045, 17314307}, {13046, 17314563}, {13047, 17314819}, + {13048, 17315075}, {13049, 17315331}, {13050, 17315587}, {13051, 17315843}, + {13052, 17316099}, {13053, 17316355}, {13054, 17316611}, {13055, 34094083}, + {13056, 67649027}, {13057, 67650051}, {13058, 67651075}, {13059, 50874883}, + {13060, 67652867}, {13061, 50876675}, {13062, 50877443}, {13063, 84432643}, + {13064, 67656707}, {13065, 50880515}, {13066, 50881283}, {13067, 50882051}, + {13068, 67660035}, {13069, 67661059}, {13070, 50884867}, {13071, 50885635}, + {13072, 34109187}, {13073, 50886915}, {13074, 67664899}, {13075, 67665923}, + {13076, 34112515}, {13077, 84444675}, {13078, 101223171}, {13079, 84447491}, + {13080, 50890755}, {13081, 84448771}, {13082, 84450051}, {13083, 67674115}, + {13084, 50897923}, {13085, 50898691}, {13086, 50899459}, {13087, 67677443}, + {13088, 84455683}, {13089, 67679747}, {13090, 50903555}, {13091, 50904323}, + {13092, 50905091}, {13093, 34128643}, {13094, 34129155}, {13095, 34117891}, + {13096, 34129667}, {13097, 50907395}, {13098, 50908163}, {13099, 84463363}, + {13100, 50910211}, {13101, 67688195}, {13102, 84466435}, {13103, 50913283}, + {13104, 34136835}, {13105, 34137347}, {13106, 84469507}, {13107, 67693571}, + {13108, 84471811}, {13109, 50918659}, {13110, 84473859}, {13111, 34143491}, + {13112, 50921219}, {13113, 50921987}, {13114, 50922755}, {13115, 50923523}, + {13116, 50924291}, {13117, 67702275}, {13118, 50926083}, {13119, 34149635}, + {13120, 50927363}, {13121, 50928131}, {13122, 50928899}, {13123, 67706883}, + {13124, 50930691}, {13125, 50931459}, {13126, 50932227}, {13127, 84487427}, + {13128, 67711491}, {13129, 34158083}, {13130, 84490243}, {13131, 34159875}, + {13132, 67714819}, {13133, 67669251}, {13134, 50938627}, {13135, 50939395}, + {13136, 50940163}, {13137, 67718147}, {13138, 34164739}, {13139, 50942467}, + {13140, 67720451}, {13141, 34167043}, {13142, 84499203}, {13143, 50893571}, + {13144, 34168835}, {13145, 34169347}, {13146, 34169859}, {13147, 34170371}, + {13148, 34170883}, {13149, 34171395}, {13150, 34171907}, {13151, 34172419}, + {13152, 34172931}, {13153, 34173443}, {13154, 50951171}, {13155, 50951939}, + {13156, 50952707}, {13157, 50953475}, {13158, 50954243}, {13159, 50955011}, + {13160, 50955779}, {13161, 50956547}, {13162, 50957315}, {13163, 50958083}, + {13164, 50958851}, {13165, 50959619}, {13166, 50960387}, {13167, 50961155}, + {13168, 50961923}, {13169, 50962691}, {13170, 34186243}, {13171, 34186755}, + {13172, 50964483}, {13173, 34188035}, {13174, 34188547}, {13175, 34189059}, + {13176, 50966787}, {13177, 50967555}, {13178, 34191107}, {13179, 34191619}, + {13180, 34192131}, {13181, 34192643}, {13182, 34193155}, {13183, 67748099}, + {13184, 34185731}, {13185, 34194691}, {13186, 34195203}, {13187, 34195715}, + {13188, 34196227}, {13189, 34196739}, {13190, 34197251}, {13191, 34197763}, + {13192, 50975491}, {13193, 67753475}, {13194, 34200067}, {13195, 34200579}, + {13196, 34201091}, {13197, 34201603}, {13198, 34202115}, {13199, 34202627}, + {13200, 34203139}, {13201, 50980867}, {13202, 50981635}, {13203, 50980099}, + {13204, 50982403}, {13205, 34205955}, {13206, 34206467}, {13207, 34206979}, + {13208, 33556995}, {13209, 34207491}, {13210, 34208003}, {13211, 34208515}, + {13212, 34209027}, {13213, 34209539}, {13214, 34210051}, {13215, 50987779}, + {13216, 50988547}, {13217, 34189827}, {13218, 50989315}, {13219, 50990083}, + {13220, 50990851}, {13221, 34190595}, {13222, 50991619}, {13223, 50992387}, + {13224, 67770371}, {13225, 34185731}, {13226, 50994179}, {13227, 50994947}, + {13228, 50995715}, {13229, 50996483}, {13230, 84551683}, {13231, 101330179}, + {13232, 34222851}, {13233, 34223363}, {13234, 34223875}, {13235, 34224387}, + {13236, 34224899}, {13237, 34225411}, {13238, 34225923}, {13239, 34226435}, + {13240, 34226947}, {13241, 34226435}, {13242, 34227459}, {13243, 34227971}, + {13244, 34228483}, {13245, 34228995}, {13246, 34229507}, {13247, 34228995}, + {13248, 34230019}, {13249, 34230531}, {13250, 2}, {13251, 34231043}, + {13252, 33817091}, {13253, 33554947}, {13254, 67785987}, {13255, 2}, + {13256, 34232579}, {13257, 34233091}, {13258, 34233603}, {13259, 34185475}, + {13260, 34234115}, {13261, 34234627}, {13262, 34210051}, {13263, 34235139}, + {13264, 33557251}, {13265, 34235651}, {13266, 51013379}, {13267, 34236931}, + {13268, 34197251}, {13269, 51014659}, {13270, 51015427}, {13271, 34238979}, + {13272, 2}, {13273, 51016707}, {13274, 34240259}, {13275, 34221059}, + {13276, 34240771}, {13277, 34241283}, {13278, 51019011}, {13279, 51019779}, + {13280, 34243331}, {13281, 34243843}, {13282, 34244355}, {13283, 34244867}, + {13284, 34245379}, {13285, 34245891}, {13286, 34246403}, {13287, 34246915}, + {13288, 34247427}, {13289, 51025155}, {13290, 51025923}, {13291, 51026691}, + {13292, 51027459}, {13293, 51028227}, {13294, 51028995}, {13295, 51029763}, + {13296, 51030531}, {13297, 51031299}, {13298, 51032067}, {13299, 51032835}, + {13300, 51033603}, {13301, 51034371}, {13302, 51035139}, {13303, 51035907}, + {13304, 51036675}, {13305, 51037443}, {13306, 51038211}, {13307, 51038979}, + {13308, 51039747}, {13309, 51040515}, {13310, 51041283}, {13311, 51042051}, {13312, 1}, {42125, 2}, {42128, 1}, {42183, 2}, - {42192, 1}, {42540, 2}, {42560, 17488643}, {42561, 1}, - {42562, 17488899}, {42563, 1}, {42564, 17489155}, {42565, 1}, - {42566, 17489411}, {42567, 1}, {42568, 17489667}, {42569, 1}, - {42570, 16936451}, {42571, 1}, {42572, 17489923}, {42573, 1}, - {42574, 17490179}, {42575, 1}, {42576, 17490435}, {42577, 1}, - {42578, 17490691}, {42579, 1}, {42580, 17490947}, {42581, 1}, - {42582, 17491203}, {42583, 1}, {42584, 17491459}, {42585, 1}, - {42586, 17491715}, {42587, 1}, {42588, 17491971}, {42589, 1}, - {42590, 17492227}, {42591, 1}, {42592, 17492483}, {42593, 1}, - {42594, 17492739}, {42595, 1}, {42596, 17492995}, {42597, 1}, - {42598, 17493251}, {42599, 1}, {42600, 17493507}, {42601, 1}, - {42602, 17493763}, {42603, 1}, {42604, 17494019}, {42605, 1}, - {42624, 17494275}, {42625, 1}, {42626, 17494531}, {42627, 1}, - {42628, 17494787}, {42629, 1}, {42630, 17495043}, {42631, 1}, - {42632, 17495299}, {42633, 1}, {42634, 17495555}, {42635, 1}, - {42636, 17495811}, {42637, 1}, {42638, 17496067}, {42639, 1}, - {42640, 17496323}, {42641, 1}, {42642, 17496579}, {42643, 1}, - {42644, 17496835}, {42645, 1}, {42646, 17497091}, {42647, 1}, - {42648, 17497347}, {42649, 1}, {42650, 17497603}, {42651, 1}, + {42192, 1}, {42540, 2}, {42560, 17488387}, {42561, 1}, + {42562, 17488643}, {42563, 1}, {42564, 17488899}, {42565, 1}, + {42566, 17489155}, {42567, 1}, {42568, 17489411}, {42569, 1}, + {42570, 16936451}, {42571, 1}, {42572, 17489667}, {42573, 1}, + {42574, 17489923}, {42575, 1}, {42576, 17490179}, {42577, 1}, + {42578, 17490435}, {42579, 1}, {42580, 17490691}, {42581, 1}, + {42582, 17490947}, {42583, 1}, {42584, 17491203}, {42585, 1}, + {42586, 17491459}, {42587, 1}, {42588, 17491715}, {42589, 1}, + {42590, 17491971}, {42591, 1}, {42592, 17492227}, {42593, 1}, + {42594, 17492483}, {42595, 1}, {42596, 17492739}, {42597, 1}, + {42598, 17492995}, {42599, 1}, {42600, 17493251}, {42601, 1}, + {42602, 17493507}, {42603, 1}, {42604, 17493763}, {42605, 1}, + {42624, 17494019}, {42625, 1}, {42626, 17494275}, {42627, 1}, + {42628, 17494531}, {42629, 1}, {42630, 17494787}, {42631, 1}, + {42632, 17495043}, {42633, 1}, {42634, 17495299}, {42635, 1}, + {42636, 17495555}, {42637, 1}, {42638, 17495811}, {42639, 1}, + {42640, 17496067}, {42641, 1}, {42642, 17496323}, {42643, 1}, + {42644, 17496579}, {42645, 1}, {42646, 17496835}, {42647, 1}, + {42648, 17497091}, {42649, 1}, {42650, 17497347}, {42651, 1}, {42652, 16873219}, {42653, 16873731}, {42654, 1}, {42744, 2}, - {42752, 1}, {42786, 17497859}, {42787, 1}, {42788, 17498115}, - {42789, 1}, {42790, 17498371}, {42791, 1}, {42792, 17498627}, - {42793, 1}, {42794, 17498883}, {42795, 1}, {42796, 17499139}, - {42797, 1}, {42798, 17499395}, {42799, 1}, {42802, 17499651}, - {42803, 1}, {42804, 17499907}, {42805, 1}, {42806, 17500163}, - {42807, 1}, {42808, 17500419}, {42809, 1}, {42810, 17500675}, - {42811, 1}, {42812, 17500931}, {42813, 1}, {42814, 17501187}, - {42815, 1}, {42816, 17501443}, {42817, 1}, {42818, 17501699}, - {42819, 1}, {42820, 17501955}, {42821, 1}, {42822, 17502211}, - {42823, 1}, {42824, 17502467}, {42825, 1}, {42826, 17502723}, - {42827, 1}, {42828, 17502979}, {42829, 1}, {42830, 17503235}, - {42831, 1}, {42832, 17503491}, {42833, 1}, {42834, 17503747}, - {42835, 1}, {42836, 17504003}, {42837, 1}, {42838, 17504259}, - {42839, 1}, {42840, 17504515}, {42841, 1}, {42842, 17504771}, - {42843, 1}, {42844, 17505027}, {42845, 1}, {42846, 17505283}, - {42847, 1}, {42848, 17505539}, {42849, 1}, {42850, 17505795}, - {42851, 1}, {42852, 17506051}, {42853, 1}, {42854, 17506307}, - {42855, 1}, {42856, 17506563}, {42857, 1}, {42858, 17506819}, - {42859, 1}, {42860, 17507075}, {42861, 1}, {42862, 17507331}, - {42863, 1}, {42864, 17507331}, {42865, 1}, {42873, 17507587}, - {42874, 1}, {42875, 17507843}, {42876, 1}, {42877, 17508099}, - {42878, 17508355}, {42879, 1}, {42880, 17508611}, {42881, 1}, - {42882, 17508867}, {42883, 1}, {42884, 17509123}, {42885, 1}, - {42886, 17509379}, {42887, 1}, {42891, 17509635}, {42892, 1}, - {42893, 16951299}, {42894, 1}, {42896, 17509891}, {42897, 1}, - {42898, 17510147}, {42899, 1}, {42902, 17510403}, {42903, 1}, - {42904, 17510659}, {42905, 1}, {42906, 17510915}, {42907, 1}, - {42908, 17511171}, {42909, 1}, {42910, 17511427}, {42911, 1}, - {42912, 17511683}, {42913, 1}, {42914, 17511939}, {42915, 1}, - {42916, 17512195}, {42917, 1}, {42918, 17512451}, {42919, 1}, - {42920, 17512707}, {42921, 1}, {42922, 16841475}, {42923, 16948995}, - {42924, 16951043}, {42925, 17512963}, {42926, 16951555}, {42927, 1}, - {42928, 17513219}, {42929, 17513475}, {42930, 16952067}, {42931, 17513731}, - {42932, 17513987}, {42933, 1}, {42934, 17514243}, {42935, 1}, - {42936, 17514499}, {42937, 1}, {42938, 17514755}, {42939, 1}, - {42940, 17515011}, {42941, 1}, {42942, 17515267}, {42943, 1}, - {42944, 17515523}, {42945, 1}, {42946, 17515779}, {42947, 1}, - {42948, 17516035}, {42949, 16954371}, {42950, 17516291}, {42951, 17516547}, - {42952, 1}, {42953, 17516803}, {42954, 1}, {42955, 2}, - {42960, 17517059}, {42961, 1}, {42962, 2}, {42963, 1}, - {42964, 2}, {42965, 1}, {42966, 17517315}, {42967, 1}, - {42968, 17517571}, {42969, 1}, {42970, 2}, {42994, 16777731}, - {42995, 16778499}, {42996, 16781315}, {42997, 17517827}, {42998, 1}, + {42752, 1}, {42786, 17497603}, {42787, 1}, {42788, 17497859}, + {42789, 1}, {42790, 17498115}, {42791, 1}, {42792, 17498371}, + {42793, 1}, {42794, 17498627}, {42795, 1}, {42796, 17498883}, + {42797, 1}, {42798, 17499139}, {42799, 1}, {42802, 17499395}, + {42803, 1}, {42804, 17499651}, {42805, 1}, {42806, 17499907}, + {42807, 1}, {42808, 17500163}, {42809, 1}, {42810, 17500419}, + {42811, 1}, {42812, 17500675}, {42813, 1}, {42814, 17500931}, + {42815, 1}, {42816, 17501187}, {42817, 1}, {42818, 17501443}, + {42819, 1}, {42820, 17501699}, {42821, 1}, {42822, 17501955}, + {42823, 1}, {42824, 17502211}, {42825, 1}, {42826, 17502467}, + {42827, 1}, {42828, 17502723}, {42829, 1}, {42830, 17502979}, + {42831, 1}, {42832, 17503235}, {42833, 1}, {42834, 17503491}, + {42835, 1}, {42836, 17503747}, {42837, 1}, {42838, 17504003}, + {42839, 1}, {42840, 17504259}, {42841, 1}, {42842, 17504515}, + {42843, 1}, {42844, 17504771}, {42845, 1}, {42846, 17505027}, + {42847, 1}, {42848, 17505283}, {42849, 1}, {42850, 17505539}, + {42851, 1}, {42852, 17505795}, {42853, 1}, {42854, 17506051}, + {42855, 1}, {42856, 17506307}, {42857, 1}, {42858, 17506563}, + {42859, 1}, {42860, 17506819}, {42861, 1}, {42862, 17507075}, + {42863, 1}, {42864, 17507075}, {42865, 1}, {42873, 17507331}, + {42874, 1}, {42875, 17507587}, {42876, 1}, {42877, 17507843}, + {42878, 17508099}, {42879, 1}, {42880, 17508355}, {42881, 1}, + {42882, 17508611}, {42883, 1}, {42884, 17508867}, {42885, 1}, + {42886, 17509123}, {42887, 1}, {42891, 17509379}, {42892, 1}, + {42893, 16951299}, {42894, 1}, {42896, 17509635}, {42897, 1}, + {42898, 17509891}, {42899, 1}, {42902, 17510147}, {42903, 1}, + {42904, 17510403}, {42905, 1}, {42906, 17510659}, {42907, 1}, + {42908, 17510915}, {42909, 1}, {42910, 17511171}, {42911, 1}, + {42912, 17511427}, {42913, 1}, {42914, 17511683}, {42915, 1}, + {42916, 17511939}, {42917, 1}, {42918, 17512195}, {42919, 1}, + {42920, 17512451}, {42921, 1}, {42922, 16841475}, {42923, 16948995}, + {42924, 16951043}, {42925, 17512707}, {42926, 16951555}, {42927, 1}, + {42928, 17512963}, {42929, 17513219}, {42930, 16952067}, {42931, 17513475}, + {42932, 17513731}, {42933, 1}, {42934, 17513987}, {42935, 1}, + {42936, 17514243}, {42937, 1}, {42938, 17514499}, {42939, 1}, + {42940, 17514755}, {42941, 1}, {42942, 17515011}, {42943, 1}, + {42944, 17515267}, {42945, 1}, {42946, 17515523}, {42947, 1}, + {42948, 17515779}, {42949, 16954371}, {42950, 17516035}, {42951, 17516291}, + {42952, 1}, {42953, 17516547}, {42954, 1}, {42955, 2}, + {42960, 17516803}, {42961, 1}, {42962, 2}, {42963, 1}, + {42964, 2}, {42965, 1}, {42966, 17517059}, {42967, 1}, + {42968, 17517315}, {42969, 1}, {42970, 2}, {42994, 16777731}, + {42995, 16778499}, {42996, 16781315}, {42997, 17517571}, {42998, 1}, {43000, 16802051}, {43001, 16808195}, {43002, 1}, {43053, 2}, {43056, 1}, {43066, 2}, {43072, 1}, {43128, 2}, {43136, 1}, {43206, 2}, {43214, 1}, {43226, 2}, @@ -1631,375 +1632,375 @@ const uint32_t table[8000][2] = {43612, 1}, {43715, 2}, {43739, 1}, {43767, 2}, {43777, 1}, {43783, 2}, {43785, 1}, {43791, 2}, {43793, 1}, {43799, 2}, {43808, 1}, {43815, 2}, - {43816, 1}, {43823, 2}, {43824, 1}, {43868, 17498371}, - {43869, 17518083}, {43870, 17124867}, {43871, 17518339}, {43872, 1}, - {43881, 17518595}, {43882, 1}, {43884, 2}, {43888, 17518851}, - {43889, 17519107}, {43890, 17519363}, {43891, 17519619}, {43892, 17519875}, - {43893, 17520131}, {43894, 17520387}, {43895, 17520643}, {43896, 17520899}, - {43897, 17521155}, {43898, 17521411}, {43899, 17521667}, {43900, 17521923}, - {43901, 17522179}, {43902, 17522435}, {43903, 17522691}, {43904, 17522947}, - {43905, 17523203}, {43906, 17523459}, {43907, 17523715}, {43908, 17523971}, - {43909, 17524227}, {43910, 17524483}, {43911, 17524739}, {43912, 17524995}, - {43913, 17525251}, {43914, 17525507}, {43915, 17525763}, {43916, 17526019}, - {43917, 17526275}, {43918, 17526531}, {43919, 17526787}, {43920, 17527043}, - {43921, 17527299}, {43922, 17527555}, {43923, 17527811}, {43924, 17528067}, - {43925, 17528323}, {43926, 17528579}, {43927, 17528835}, {43928, 17529091}, - {43929, 17529347}, {43930, 17529603}, {43931, 17529859}, {43932, 17530115}, - {43933, 17530371}, {43934, 17530627}, {43935, 17530883}, {43936, 17531139}, - {43937, 17531395}, {43938, 17531651}, {43939, 17531907}, {43940, 17532163}, - {43941, 17532419}, {43942, 17532675}, {43943, 17532931}, {43944, 17533187}, - {43945, 17533443}, {43946, 17533699}, {43947, 17533955}, {43948, 17534211}, - {43949, 17534467}, {43950, 17534723}, {43951, 17534979}, {43952, 17535235}, - {43953, 17535491}, {43954, 17535747}, {43955, 17536003}, {43956, 17536259}, - {43957, 17536515}, {43958, 17536771}, {43959, 17537027}, {43960, 17537283}, - {43961, 17537539}, {43962, 17537795}, {43963, 17538051}, {43964, 17538307}, - {43965, 17538563}, {43966, 17538819}, {43967, 17539075}, {43968, 1}, + {43816, 1}, {43823, 2}, {43824, 1}, {43868, 17498115}, + {43869, 17517827}, {43870, 17124611}, {43871, 17518083}, {43872, 1}, + {43881, 17518339}, {43882, 1}, {43884, 2}, {43888, 17518595}, + {43889, 17518851}, {43890, 17519107}, {43891, 17519363}, {43892, 17519619}, + {43893, 17519875}, {43894, 17520131}, {43895, 17520387}, {43896, 17520643}, + {43897, 17520899}, {43898, 17521155}, {43899, 17521411}, {43900, 17521667}, + {43901, 17521923}, {43902, 17522179}, {43903, 17522435}, {43904, 17522691}, + {43905, 17522947}, {43906, 17523203}, {43907, 17523459}, {43908, 17523715}, + {43909, 17523971}, {43910, 17524227}, {43911, 17524483}, {43912, 17524739}, + {43913, 17524995}, {43914, 17525251}, {43915, 17525507}, {43916, 17525763}, + {43917, 17526019}, {43918, 17526275}, {43919, 17526531}, {43920, 17526787}, + {43921, 17527043}, {43922, 17527299}, {43923, 17527555}, {43924, 17527811}, + {43925, 17528067}, {43926, 17528323}, {43927, 17528579}, {43928, 17528835}, + {43929, 17529091}, {43930, 17529347}, {43931, 17529603}, {43932, 17529859}, + {43933, 17530115}, {43934, 17530371}, {43935, 17530627}, {43936, 17530883}, + {43937, 17531139}, {43938, 17531395}, {43939, 17531651}, {43940, 17531907}, + {43941, 17532163}, {43942, 17532419}, {43943, 17532675}, {43944, 17532931}, + {43945, 17533187}, {43946, 17533443}, {43947, 17533699}, {43948, 17533955}, + {43949, 17534211}, {43950, 17534467}, {43951, 17534723}, {43952, 17534979}, + {43953, 17535235}, {43954, 17535491}, {43955, 17535747}, {43956, 17536003}, + {43957, 17536259}, {43958, 17536515}, {43959, 17536771}, {43960, 17537027}, + {43961, 17537283}, {43962, 17537539}, {43963, 17537795}, {43964, 17538051}, + {43965, 17538307}, {43966, 17538563}, {43967, 17538819}, {43968, 1}, {44014, 2}, {44016, 1}, {44026, 2}, {44032, 1}, {55204, 2}, {55216, 1}, {55239, 2}, {55243, 1}, - {55292, 2}, {63744, 17539331}, {63745, 17539587}, {63746, 17182211}, - {63747, 17539843}, {63748, 17540099}, {63749, 17540355}, {63750, 17540611}, - {63751, 17196035}, {63753, 17540867}, {63754, 17184259}, {63755, 17541123}, - {63756, 17541379}, {63757, 17541635}, {63758, 17541891}, {63759, 17542147}, - {63760, 17542403}, {63761, 17542659}, {63762, 17542915}, {63763, 17543171}, - {63764, 17543427}, {63765, 17543683}, {63766, 17543939}, {63767, 17544195}, - {63768, 17544451}, {63769, 17544707}, {63770, 17544963}, {63771, 17545219}, - {63772, 17545475}, {63773, 17545731}, {63774, 17545987}, {63775, 17546243}, - {63776, 17546499}, {63777, 17546755}, {63778, 17547011}, {63779, 17547267}, - {63780, 17547523}, {63781, 17547779}, {63782, 17548035}, {63783, 17548291}, - {63784, 17548547}, {63785, 17548803}, {63786, 17549059}, {63787, 17549315}, - {63788, 17549571}, {63789, 17549827}, {63790, 17550083}, {63791, 17550339}, - {63792, 17550595}, {63793, 17550851}, {63794, 17551107}, {63795, 17551363}, - {63796, 17173507}, {63797, 17551619}, {63798, 17551875}, {63799, 17552131}, - {63800, 17552387}, {63801, 17552643}, {63802, 17552899}, {63803, 17553155}, - {63804, 17553411}, {63805, 17553667}, {63806, 17553923}, {63807, 17554179}, - {63808, 17192195}, {63809, 17554435}, {63810, 17554691}, {63811, 17554947}, - {63812, 17555203}, {63813, 17555459}, {63814, 17555715}, {63815, 17555971}, - {63816, 17556227}, {63817, 17556483}, {63818, 17556739}, {63819, 17556995}, - {63820, 17557251}, {63821, 17557507}, {63822, 17557763}, {63823, 17558019}, - {63824, 17558275}, {63825, 17558531}, {63826, 17558787}, {63827, 17559043}, - {63828, 17559299}, {63829, 17559555}, {63830, 17559811}, {63831, 17560067}, - {63832, 17560323}, {63833, 17560579}, {63834, 17560835}, {63835, 17561091}, - {63836, 17543427}, {63837, 17561347}, {63838, 17561603}, {63839, 17561859}, - {63840, 17562115}, {63841, 17562371}, {63842, 17562627}, {63843, 17562883}, - {63844, 17563139}, {63845, 17563395}, {63846, 17563651}, {63847, 17563907}, - {63848, 17564163}, {63849, 17564419}, {63850, 17564675}, {63851, 17564931}, - {63852, 17565187}, {63853, 17565443}, {63854, 17565699}, {63855, 17565955}, - {63856, 17566211}, {63857, 17182723}, {63858, 17566467}, {63859, 17566723}, - {63860, 17566979}, {63861, 17567235}, {63862, 17567491}, {63863, 17567747}, - {63864, 17568003}, {63865, 17568259}, {63866, 17568515}, {63867, 17568771}, - {63868, 17569027}, {63869, 17569283}, {63870, 17569539}, {63871, 17569795}, - {63872, 17570051}, {63873, 17151235}, {63874, 17570307}, {63875, 17570563}, - {63876, 17570819}, {63877, 17571075}, {63878, 17571331}, {63879, 17571587}, - {63880, 17571843}, {63881, 17572099}, {63882, 17146371}, {63883, 17572355}, - {63884, 17572611}, {63885, 17572867}, {63886, 17573123}, {63887, 17573379}, - {63888, 17573635}, {63889, 17573891}, {63890, 17574147}, {63891, 17574403}, - {63892, 17574659}, {63893, 17574915}, {63894, 17575171}, {63895, 17575427}, - {63896, 17575683}, {63897, 17575939}, {63898, 17576195}, {63899, 17576451}, - {63900, 17576707}, {63901, 17576963}, {63902, 17577219}, {63903, 17577475}, - {63904, 17577731}, {63905, 17565955}, {63906, 17577987}, {63907, 17578243}, - {63908, 17578499}, {63909, 17578755}, {63910, 17579011}, {63911, 17579267}, - {63912, 17317123}, {63913, 17579523}, {63914, 17561859}, {63915, 17579779}, - {63916, 17580035}, {63917, 17580291}, {63918, 17580547}, {63919, 17580803}, - {63920, 17581059}, {63921, 17581315}, {63922, 17581571}, {63923, 17581827}, - {63924, 17582083}, {63925, 17582339}, {63926, 17582595}, {63927, 17582851}, - {63928, 17583107}, {63929, 17583363}, {63930, 17583619}, {63931, 17583875}, - {63932, 17584131}, {63933, 17584387}, {63934, 17584643}, {63935, 17543427}, - {63936, 17584899}, {63937, 17585155}, {63938, 17585411}, {63939, 17585667}, - {63940, 17195779}, {63941, 17585923}, {63942, 17586179}, {63943, 17586435}, - {63944, 17586691}, {63945, 17586947}, {63946, 17587203}, {63947, 17587459}, - {63948, 17587715}, {63949, 17587971}, {63950, 17588227}, {63951, 17588483}, - {63952, 17588739}, {63953, 17254403}, {63954, 17588995}, {63955, 17589251}, - {63956, 17589507}, {63957, 17589763}, {63958, 17590019}, {63959, 17590275}, - {63960, 17590531}, {63961, 17590787}, {63962, 17591043}, {63963, 17562371}, - {63964, 17591299}, {63965, 17591555}, {63966, 17591811}, {63967, 17592067}, - {63968, 17592323}, {63969, 17592579}, {63970, 17592835}, {63971, 17593091}, - {63972, 17593347}, {63973, 17593603}, {63974, 17593859}, {63975, 17594115}, - {63976, 17594371}, {63977, 17184003}, {63978, 17594627}, {63979, 17594883}, - {63980, 17595139}, {63981, 17595395}, {63982, 17595651}, {63983, 17595907}, - {63984, 17596163}, {63985, 17596419}, {63986, 17596675}, {63987, 17596931}, - {63988, 17597187}, {63989, 17597443}, {63990, 17597699}, {63991, 17171459}, - {63992, 17597955}, {63993, 17598211}, {63994, 17598467}, {63995, 17598723}, - {63996, 17598979}, {63997, 17599235}, {63998, 17599491}, {63999, 17599747}, - {64000, 17600003}, {64001, 17600259}, {64002, 17600515}, {64003, 17600771}, - {64004, 17601027}, {64005, 17601283}, {64006, 17601539}, {64007, 17601795}, - {64008, 17178371}, {64009, 17602051}, {64010, 17179139}, {64011, 17602307}, - {64012, 17602563}, {64013, 17602819}, {64014, 1}, {64016, 17603075}, - {64017, 1}, {64018, 17603331}, {64019, 1}, {64021, 17603587}, - {64022, 17603843}, {64023, 17604099}, {64024, 17604355}, {64025, 17604611}, - {64026, 17604867}, {64027, 17605123}, {64028, 17605379}, {64029, 17605635}, - {64030, 17173251}, {64031, 1}, {64032, 17605891}, {64033, 1}, - {64034, 17606147}, {64035, 1}, {64037, 17606403}, {64038, 17606659}, - {64039, 1}, {64042, 17606915}, {64043, 17607171}, {64044, 17607427}, - {64045, 17607683}, {64046, 17607939}, {64047, 17608195}, {64048, 17608451}, - {64049, 17608707}, {64050, 17608963}, {64051, 17609219}, {64052, 17609475}, - {64053, 17609731}, {64054, 17609987}, {64055, 17610243}, {64056, 17610499}, - {64057, 17610755}, {64058, 17611011}, {64059, 17611267}, {64060, 17153027}, - {64061, 17611523}, {64062, 17611779}, {64063, 17612035}, {64064, 17612291}, - {64065, 17612547}, {64066, 17612803}, {64067, 17613059}, {64068, 17613315}, - {64069, 17613571}, {64070, 17613827}, {64071, 17614083}, {64072, 17614339}, - {64073, 17614595}, {64074, 17614851}, {64075, 17615107}, {64076, 17265155}, - {64077, 17615363}, {64078, 17615619}, {64079, 17615875}, {64080, 17616131}, - {64081, 17268227}, {64082, 17616387}, {64083, 17616643}, {64084, 17616899}, - {64085, 17617155}, {64086, 17617411}, {64087, 17575171}, {64088, 17617667}, - {64089, 17617923}, {64090, 17618179}, {64091, 17618435}, {64092, 17618691}, - {64093, 17618947}, {64095, 17619203}, {64096, 17619459}, {64097, 17619715}, - {64098, 17619971}, {64099, 17620227}, {64100, 17620483}, {64101, 17620739}, - {64102, 17620995}, {64103, 17606403}, {64104, 17621251}, {64105, 17621507}, - {64106, 17621763}, {64107, 17622019}, {64108, 17622275}, {64109, 17622531}, - {64110, 2}, {64112, 17622787}, {64113, 17623043}, {64114, 17623299}, - {64115, 17623555}, {64116, 17623811}, {64117, 17624067}, {64118, 17624323}, - {64119, 17624579}, {64120, 17609987}, {64121, 17624835}, {64122, 17625091}, - {64123, 17625347}, {64124, 17603075}, {64125, 17625603}, {64126, 17625859}, - {64127, 17626115}, {64128, 17626371}, {64129, 17626627}, {64130, 17626883}, - {64131, 17627139}, {64132, 17627395}, {64133, 17627651}, {64134, 17627907}, - {64135, 17628163}, {64136, 17628419}, {64137, 17612035}, {64138, 17628675}, - {64139, 17612291}, {64140, 17628931}, {64141, 17629187}, {64142, 17629443}, - {64143, 17629699}, {64144, 17629955}, {64145, 17603331}, {64146, 17548803}, - {64147, 17630211}, {64148, 17630467}, {64149, 17161475}, {64150, 17566211}, - {64151, 17587203}, {64152, 17630723}, {64153, 17630979}, {64154, 17614083}, - {64155, 17631235}, {64156, 17614339}, {64157, 17631491}, {64158, 17631747}, - {64159, 17632003}, {64160, 17603843}, {64161, 17632259}, {64162, 17632515}, - {64163, 17632771}, {64164, 17633027}, {64165, 17633283}, {64166, 17604099}, - {64167, 17633539}, {64168, 17633795}, {64169, 17634051}, {64170, 17634307}, - {64171, 17634563}, {64172, 17634819}, {64173, 17617411}, {64174, 17635075}, - {64175, 17635331}, {64176, 17575171}, {64177, 17635587}, {64178, 17618435}, - {64179, 17635843}, {64180, 17636099}, {64181, 17636355}, {64182, 17636611}, - {64183, 17636867}, {64184, 17619715}, {64185, 17637123}, {64186, 17606147}, - {64187, 17637379}, {64188, 17619971}, {64189, 17561347}, {64190, 17637635}, - {64191, 17620227}, {64192, 17637891}, {64193, 17620739}, {64194, 17638147}, - {64195, 17638403}, {64196, 17638659}, {64197, 17638915}, {64198, 17639171}, - {64199, 17621251}, {64200, 17605379}, {64201, 17639427}, {64202, 17621507}, - {64203, 17639683}, {64204, 17621763}, {64205, 17639939}, {64206, 17196035}, - {64207, 17640195}, {64208, 17640451}, {64209, 17640707}, {64210, 17640963}, - {64211, 17641219}, {64212, 17641475}, {64213, 17641731}, {64214, 17641987}, - {64215, 17642243}, {64216, 17642499}, {64217, 17642755}, {64218, 2}, - {64256, 34420227}, {64257, 34420739}, {64258, 34421251}, {64259, 51197699}, - {64260, 51198979}, {64261, 33559043}, {64263, 2}, {64275, 34422531}, - {64276, 34423043}, {64277, 34423555}, {64278, 34424067}, {64279, 34424579}, - {64280, 2}, {64285, 34425091}, {64286, 1}, {64287, 34425603}, - {64288, 17648899}, {64289, 17044227}, {64290, 17044995}, {64291, 17649155}, - {64292, 17649411}, {64293, 17649667}, {64294, 17649923}, {64295, 17650179}, - {64296, 17650435}, {64297, 17037059}, {64298, 34427907}, {64299, 34428419}, - {64300, 51206147}, {64301, 51206915}, {64302, 34430467}, {64303, 34430979}, - {64304, 34431491}, {64305, 34432003}, {64306, 34432515}, {64307, 34433027}, - {64308, 34433539}, {64309, 34434051}, {64310, 34434563}, {64311, 2}, - {64312, 34435075}, {64313, 34435587}, {64314, 34436099}, {64315, 34436611}, - {64316, 34437123}, {64317, 2}, {64318, 34437635}, {64319, 2}, - {64320, 34438147}, {64321, 34438659}, {64322, 2}, {64323, 34439171}, - {64324, 34439683}, {64325, 2}, {64326, 34440195}, {64327, 34440707}, - {64328, 34441219}, {64329, 34428931}, {64330, 34441731}, {64331, 34442243}, - {64332, 34442755}, {64333, 34443267}, {64334, 34443779}, {64335, 34444291}, - {64336, 17667587}, {64338, 17667843}, {64342, 17668099}, {64346, 17668355}, - {64350, 17668611}, {64354, 17668867}, {64358, 17669123}, {64362, 17669379}, - {64366, 17669635}, {64370, 17669891}, {64374, 17670147}, {64378, 17670403}, - {64382, 17670659}, {64386, 17670915}, {64388, 17671171}, {64390, 17671427}, - {64392, 17671683}, {64394, 17671939}, {64396, 17672195}, {64398, 17672451}, - {64402, 17672707}, {64406, 17672963}, {64410, 17673219}, {64414, 17673475}, - {64416, 17673731}, {64420, 17673987}, {64422, 17674243}, {64426, 17674499}, - {64430, 17674755}, {64432, 17675011}, {64434, 1}, {64451, 2}, - {64467, 17675267}, {64471, 16911363}, {64473, 17675523}, {64475, 17675779}, - {64477, 33688579}, {64478, 17676035}, {64480, 17676291}, {64482, 17676547}, - {64484, 17676803}, {64488, 17677059}, {64490, 34454531}, {64492, 34455043}, - {64494, 34455555}, {64496, 34456067}, {64498, 34456579}, {64500, 34457091}, - {64502, 34457603}, {64505, 34458115}, {64508, 17681411}, {64512, 34458883}, - {64513, 34459395}, {64514, 34459907}, {64515, 34458115}, {64516, 34460419}, - {64517, 34460931}, {64518, 34461443}, {64519, 34461955}, {64520, 34462467}, - {64521, 34462979}, {64522, 34463491}, {64523, 34464003}, {64524, 34464515}, - {64525, 34465027}, {64526, 34465539}, {64527, 34466051}, {64528, 34466563}, - {64529, 34467075}, {64530, 34467587}, {64531, 34468099}, {64532, 34468611}, - {64533, 34469123}, {64534, 34469635}, {64535, 34469379}, {64536, 34470147}, - {64537, 34470659}, {64538, 34471171}, {64539, 34471683}, {64540, 34472195}, - {64541, 34472707}, {64542, 34473219}, {64543, 34473731}, {64544, 34474243}, - {64545, 34474755}, {64546, 34475267}, {64547, 34475779}, {64548, 34476291}, - {64549, 34476803}, {64550, 34477315}, {64551, 34477827}, {64552, 34478339}, - {64553, 34478851}, {64554, 34479363}, {64555, 34479875}, {64556, 34480387}, - {64557, 34480899}, {64558, 34481411}, {64559, 34481923}, {64560, 34482435}, - {64561, 34482947}, {64562, 34483459}, {64563, 34483971}, {64564, 34484483}, - {64565, 34484995}, {64566, 34485507}, {64567, 34486019}, {64568, 34486531}, - {64569, 34487043}, {64570, 34487555}, {64571, 34488067}, {64572, 34488579}, - {64573, 34489091}, {64574, 34489603}, {64575, 34490115}, {64576, 34490627}, - {64577, 34491139}, {64578, 34491651}, {64579, 34492163}, {64580, 34492675}, - {64581, 34493187}, {64582, 34469891}, {64583, 34470403}, {64584, 34493699}, - {64585, 34494211}, {64586, 34494723}, {64587, 34495235}, {64588, 34495747}, - {64589, 34496259}, {64590, 34496771}, {64591, 34497283}, {64592, 34497795}, - {64593, 34498307}, {64594, 34498819}, {64595, 34499331}, {64596, 34499843}, - {64597, 34468867}, {64598, 34500355}, {64599, 34500867}, {64600, 34492931}, - {64601, 34501379}, {64602, 34500099}, {64603, 34501891}, {64604, 34502403}, - {64605, 34502915}, {64606, 51280643}, {64607, 51281411}, {64608, 51282179}, - {64609, 51282947}, {64610, 51283715}, {64611, 51284483}, {64612, 34508035}, - {64613, 34508547}, {64614, 34459907}, {64615, 34509059}, {64616, 34458115}, - {64617, 34460419}, {64618, 34509571}, {64619, 34510083}, {64620, 34462467}, - {64621, 34510595}, {64622, 34462979}, {64623, 34463491}, {64624, 34511107}, - {64625, 34511619}, {64626, 34465539}, {64627, 34512131}, {64628, 34466051}, - {64629, 34466563}, {64630, 34512643}, {64631, 34513155}, {64632, 34467587}, - {64633, 34513667}, {64634, 34468099}, {64635, 34468611}, {64636, 34482947}, - {64637, 34483459}, {64638, 34484995}, {64639, 34485507}, {64640, 34486019}, - {64641, 34488067}, {64642, 34488579}, {64643, 34489091}, {64644, 34489603}, - {64645, 34491651}, {64646, 34492163}, {64647, 34492675}, {64648, 34514179}, - {64649, 34493699}, {64650, 34514691}, {64651, 34515203}, {64652, 34496771}, - {64653, 34515715}, {64654, 34497283}, {64655, 34497795}, {64656, 34502915}, - {64657, 34516227}, {64658, 34516739}, {64659, 34492931}, {64660, 34494979}, - {64661, 34501379}, {64662, 34500099}, {64663, 34458883}, {64664, 34459395}, - {64665, 34517251}, {64666, 34459907}, {64667, 34517763}, {64668, 34460931}, - {64669, 34461443}, {64670, 34461955}, {64671, 34462467}, {64672, 34518275}, - {64673, 34464003}, {64674, 34464515}, {64675, 34465027}, {64676, 34465539}, - {64677, 34518787}, {64678, 34467587}, {64679, 34469123}, {64680, 34469635}, - {64681, 34469379}, {64682, 34470147}, {64683, 34470659}, {64684, 34471683}, - {64685, 34472195}, {64686, 34472707}, {64687, 34473219}, {64688, 34473731}, - {64689, 34474243}, {64690, 34519299}, {64691, 34474755}, {64692, 34475267}, - {64693, 34475779}, {64694, 34476291}, {64695, 34476803}, {64696, 34477315}, - {64697, 34478339}, {64698, 34478851}, {64699, 34479363}, {64700, 34479875}, - {64701, 34480387}, {64702, 34480899}, {64703, 34481411}, {64704, 34481923}, - {64705, 34482435}, {64706, 34483971}, {64707, 34484483}, {64708, 34486531}, - {64709, 34487043}, {64710, 34487555}, {64711, 34488067}, {64712, 34488579}, - {64713, 34490115}, {64714, 34490627}, {64715, 34491139}, {64716, 34491651}, - {64717, 34519811}, {64718, 34493187}, {64719, 34469891}, {64720, 34470403}, - {64721, 34493699}, {64722, 34495235}, {64723, 34495747}, {64724, 34496259}, - {64725, 34496771}, {64726, 34520323}, {64727, 34498307}, {64728, 34498819}, - {64729, 34520835}, {64730, 34468867}, {64731, 34500355}, {64732, 34500867}, - {64733, 34492931}, {64734, 34498051}, {64735, 34459907}, {64736, 34517763}, - {64737, 34462467}, {64738, 34518275}, {64739, 34465539}, {64740, 34518787}, - {64741, 34467587}, {64742, 34521347}, {64743, 34473731}, {64744, 34521859}, - {64745, 34522371}, {64746, 34522883}, {64747, 34488067}, {64748, 34488579}, - {64749, 34491651}, {64750, 34496771}, {64751, 34520323}, {64752, 34492931}, - {64753, 34498051}, {64754, 51300611}, {64755, 51301379}, {64756, 51302147}, - {64757, 34525699}, {64758, 34526211}, {64759, 34526723}, {64760, 34527235}, - {64761, 34527747}, {64762, 34528259}, {64763, 34528771}, {64764, 34529283}, - {64765, 34529795}, {64766, 34530307}, {64767, 34530819}, {64768, 34500611}, - {64769, 34531331}, {64770, 34531843}, {64771, 34532355}, {64772, 34501123}, - {64773, 34532867}, {64774, 34533379}, {64775, 34533891}, {64776, 34534403}, - {64777, 34534915}, {64778, 34535427}, {64779, 34535939}, {64780, 34522371}, - {64781, 34536451}, {64782, 34536963}, {64783, 34537475}, {64784, 34537987}, - {64785, 34525699}, {64786, 34526211}, {64787, 34526723}, {64788, 34527235}, - {64789, 34527747}, {64790, 34528259}, {64791, 34528771}, {64792, 34529283}, - {64793, 34529795}, {64794, 34530307}, {64795, 34530819}, {64796, 34500611}, - {64797, 34531331}, {64798, 34531843}, {64799, 34532355}, {64800, 34501123}, - {64801, 34532867}, {64802, 34533379}, {64803, 34533891}, {64804, 34534403}, - {64805, 34534915}, {64806, 34535427}, {64807, 34535939}, {64808, 34522371}, - {64809, 34536451}, {64810, 34536963}, {64811, 34537475}, {64812, 34537987}, - {64813, 34534915}, {64814, 34535427}, {64815, 34535939}, {64816, 34522371}, - {64817, 34521859}, {64818, 34522883}, {64819, 34477827}, {64820, 34472195}, - {64821, 34472707}, {64822, 34473219}, {64823, 34534915}, {64824, 34535427}, - {64825, 34535939}, {64826, 34477827}, {64827, 34478339}, {64828, 34538499}, - {64830, 1}, {64848, 51316227}, {64849, 51316995}, {64851, 51317763}, - {64852, 51318531}, {64853, 51319299}, {64854, 51320067}, {64855, 51320835}, - {64856, 51246851}, {64858, 51321603}, {64859, 51322371}, {64860, 51323139}, - {64861, 51323907}, {64862, 51324675}, {64863, 51325443}, {64865, 51326211}, - {64866, 51326979}, {64868, 51327747}, {64870, 51328515}, {64871, 51329283}, - {64873, 51330051}, {64874, 51330819}, {64876, 51331587}, {64878, 51332355}, - {64879, 51333123}, {64881, 51333891}, {64883, 51334659}, {64884, 51335427}, - {64885, 51336195}, {64886, 51336963}, {64888, 51337731}, {64889, 51338499}, - {64890, 51339267}, {64891, 51340035}, {64892, 51340803}, {64894, 51341571}, - {64895, 51342339}, {64896, 51343107}, {64897, 51343875}, {64898, 51344643}, - {64899, 51345411}, {64901, 51346179}, {64903, 51346947}, {64905, 51347715}, - {64906, 51247107}, {64907, 51348483}, {64908, 51349251}, {64909, 51270403}, - {64910, 51247619}, {64911, 51350019}, {64912, 2}, {64914, 51350787}, - {64915, 51351555}, {64916, 51352323}, {64917, 51353091}, {64918, 51353859}, - {64919, 51354627}, {64921, 51355395}, {64922, 51356163}, {64923, 51356931}, - {64924, 51357699}, {64926, 51358467}, {64927, 51359235}, {64928, 51360003}, - {64929, 51360771}, {64930, 51361539}, {64931, 51362307}, {64932, 51363075}, - {64933, 51363843}, {64934, 51364611}, {64935, 51365379}, {64936, 51366147}, - {64937, 51366915}, {64938, 51367683}, {64939, 51368451}, {64940, 51369219}, - {64941, 51369987}, {64942, 51277571}, {64943, 51370755}, {64944, 51371523}, - {64945, 51372291}, {64946, 51373059}, {64947, 51373827}, {64948, 51341571}, - {64949, 51343107}, {64950, 51374595}, {64951, 51375363}, {64952, 51376131}, - {64953, 51376899}, {64954, 51377667}, {64955, 51378435}, {64956, 51377667}, - {64957, 51376131}, {64958, 51379203}, {64959, 51379971}, {64960, 51380739}, - {64961, 51381507}, {64962, 51382275}, {64963, 51378435}, {64964, 51336195}, - {64965, 51328515}, {64966, 51383043}, {64967, 51383811}, {64968, 2}, - {64975, 1}, {64976, 2}, {65008, 51384579}, {65009, 51385347}, - {65010, 68163331}, {65011, 68164355}, {65012, 68165379}, {65013, 68166403}, - {65014, 68167427}, {65015, 68168451}, {65016, 68169475}, {65017, 51393283}, - {65018, 303052291}, {65019, 135284739}, {65020, 68177923}, {65021, 1}, - {65024, 0}, {65040, 17847299}, {65041, 17847555}, {65042, 2}, - {65043, 17110531}, {65044, 16848643}, {65045, 17032963}, {65046, 17033987}, - {65047, 17847811}, {65048, 17848067}, {65049, 2}, {65056, 1}, - {65072, 2}, {65073, 17848323}, {65074, 17848579}, {65075, 17848835}, - {65077, 17037827}, {65078, 17038083}, {65079, 17849091}, {65080, 17849347}, - {65081, 17849603}, {65082, 17849859}, {65083, 17850115}, {65084, 17850371}, - {65085, 17850627}, {65086, 17850883}, {65087, 17067267}, {65088, 17067523}, - {65089, 17851139}, {65090, 17851395}, {65091, 17851651}, {65092, 17851907}, - {65093, 1}, {65095, 17852163}, {65096, 17852419}, {65097, 33810691}, - {65101, 17848835}, {65104, 17847299}, {65105, 17847555}, {65106, 2}, - {65108, 16848643}, {65109, 17110531}, {65110, 17033987}, {65111, 17032963}, - {65112, 17848323}, {65113, 17037827}, {65114, 17038083}, {65115, 17849091}, - {65116, 17849347}, {65117, 17849603}, {65118, 17849859}, {65119, 17852675}, - {65120, 17852931}, {65121, 17853187}, {65122, 17037059}, {65123, 17853443}, - {65124, 17853699}, {65125, 17853955}, {65126, 17037571}, {65127, 2}, - {65128, 17854211}, {65129, 17854467}, {65130, 17854723}, {65131, 17854979}, - {65132, 2}, {65136, 34632451}, {65137, 34632963}, {65138, 34503427}, - {65139, 1}, {65140, 34504195}, {65141, 2}, {65142, 34504963}, - {65143, 34523395}, {65144, 34505731}, {65145, 34524163}, {65146, 34506499}, - {65147, 34524931}, {65148, 34507267}, {65149, 34633475}, {65150, 34633987}, - {65151, 34634499}, {65152, 17857795}, {65153, 17858051}, {65155, 17858307}, - {65157, 17858563}, {65159, 17858819}, {65161, 17677315}, {65165, 16910339}, - {65167, 17683715}, {65171, 17859075}, {65173, 17686787}, {65177, 17689859}, - {65181, 17681923}, {65185, 17682435}, {65189, 17684995}, {65193, 17834499}, - {65195, 17724675}, {65197, 17725187}, {65199, 17731587}, {65201, 17694979}, - {65205, 17745155}, {65209, 17697027}, {65213, 17698051}, {65217, 17700099}, - {65221, 17701123}, {65225, 17701635}, {65229, 17702659}, {65233, 17703683}, - {65237, 17706755}, {65241, 17708803}, {65245, 17711107}, {65249, 17682947}, - {65253, 17718019}, {65257, 17721091}, {65261, 16910851}, {65263, 17677059}, - {65265, 16911875}, {65269, 34636547}, {65271, 34637059}, {65273, 34637571}, - {65275, 34622467}, {65277, 2}, {65279, 0}, {65280, 2}, - {65281, 17032963}, {65282, 17860867}, {65283, 17852675}, {65284, 17854467}, - {65285, 17854723}, {65286, 17852931}, {65287, 17861123}, {65288, 17037827}, - {65289, 17038083}, {65290, 17853187}, {65291, 17037059}, {65292, 17847299}, - {65293, 17853443}, {65294, 17196547}, {65295, 17038595}, {65296, 17035523}, + {55292, 2}, {63744, 17539075}, {63745, 17539331}, {63746, 17181955}, + {63747, 17539587}, {63748, 17539843}, {63749, 17540099}, {63750, 17540355}, + {63751, 17195779}, {63753, 17540611}, {63754, 17184003}, {63755, 17540867}, + {63756, 17541123}, {63757, 17541379}, {63758, 17541635}, {63759, 17541891}, + {63760, 17542147}, {63761, 17542403}, {63762, 17542659}, {63763, 17542915}, + {63764, 17543171}, {63765, 17543427}, {63766, 17543683}, {63767, 17543939}, + {63768, 17544195}, {63769, 17544451}, {63770, 17544707}, {63771, 17544963}, + {63772, 17545219}, {63773, 17545475}, {63774, 17545731}, {63775, 17545987}, + {63776, 17546243}, {63777, 17546499}, {63778, 17546755}, {63779, 17547011}, + {63780, 17547267}, {63781, 17547523}, {63782, 17547779}, {63783, 17548035}, + {63784, 17548291}, {63785, 17548547}, {63786, 17548803}, {63787, 17549059}, + {63788, 17549315}, {63789, 17549571}, {63790, 17549827}, {63791, 17550083}, + {63792, 17550339}, {63793, 17550595}, {63794, 17550851}, {63795, 17551107}, + {63796, 17173251}, {63797, 17551363}, {63798, 17551619}, {63799, 17551875}, + {63800, 17552131}, {63801, 17552387}, {63802, 17552643}, {63803, 17552899}, + {63804, 17553155}, {63805, 17553411}, {63806, 17553667}, {63807, 17553923}, + {63808, 17191939}, {63809, 17554179}, {63810, 17554435}, {63811, 17554691}, + {63812, 17554947}, {63813, 17555203}, {63814, 17555459}, {63815, 17555715}, + {63816, 17555971}, {63817, 17556227}, {63818, 17556483}, {63819, 17556739}, + {63820, 17556995}, {63821, 17557251}, {63822, 17557507}, {63823, 17557763}, + {63824, 17558019}, {63825, 17558275}, {63826, 17558531}, {63827, 17558787}, + {63828, 17559043}, {63829, 17559299}, {63830, 17559555}, {63831, 17559811}, + {63832, 17560067}, {63833, 17560323}, {63834, 17560579}, {63835, 17560835}, + {63836, 17543171}, {63837, 17561091}, {63838, 17561347}, {63839, 17561603}, + {63840, 17561859}, {63841, 17562115}, {63842, 17562371}, {63843, 17562627}, + {63844, 17562883}, {63845, 17563139}, {63846, 17563395}, {63847, 17563651}, + {63848, 17563907}, {63849, 17564163}, {63850, 17564419}, {63851, 17564675}, + {63852, 17564931}, {63853, 17565187}, {63854, 17565443}, {63855, 17565699}, + {63856, 17565955}, {63857, 17182467}, {63858, 17566211}, {63859, 17566467}, + {63860, 17566723}, {63861, 17566979}, {63862, 17567235}, {63863, 17567491}, + {63864, 17567747}, {63865, 17568003}, {63866, 17568259}, {63867, 17568515}, + {63868, 17568771}, {63869, 17569027}, {63870, 17569283}, {63871, 17569539}, + {63872, 17569795}, {63873, 17150979}, {63874, 17570051}, {63875, 17570307}, + {63876, 17570563}, {63877, 17570819}, {63878, 17571075}, {63879, 17571331}, + {63880, 17571587}, {63881, 17571843}, {63882, 17146115}, {63883, 17572099}, + {63884, 17572355}, {63885, 17572611}, {63886, 17572867}, {63887, 17573123}, + {63888, 17573379}, {63889, 17573635}, {63890, 17573891}, {63891, 17574147}, + {63892, 17574403}, {63893, 17574659}, {63894, 17574915}, {63895, 17575171}, + {63896, 17575427}, {63897, 17575683}, {63898, 17575939}, {63899, 17576195}, + {63900, 17576451}, {63901, 17576707}, {63902, 17576963}, {63903, 17577219}, + {63904, 17577475}, {63905, 17565699}, {63906, 17577731}, {63907, 17577987}, + {63908, 17578243}, {63909, 17578499}, {63910, 17578755}, {63911, 17579011}, + {63912, 17316867}, {63913, 17579267}, {63914, 17561603}, {63915, 17579523}, + {63916, 17579779}, {63917, 17580035}, {63918, 17580291}, {63919, 17580547}, + {63920, 17580803}, {63921, 17581059}, {63922, 17581315}, {63923, 17581571}, + {63924, 17581827}, {63925, 17582083}, {63926, 17582339}, {63927, 17582595}, + {63928, 17582851}, {63929, 17583107}, {63930, 17583363}, {63931, 17583619}, + {63932, 17583875}, {63933, 17584131}, {63934, 17584387}, {63935, 17543171}, + {63936, 17584643}, {63937, 17584899}, {63938, 17585155}, {63939, 17585411}, + {63940, 17195523}, {63941, 17585667}, {63942, 17585923}, {63943, 17586179}, + {63944, 17586435}, {63945, 17586691}, {63946, 17586947}, {63947, 17587203}, + {63948, 17587459}, {63949, 17587715}, {63950, 17587971}, {63951, 17588227}, + {63952, 17588483}, {63953, 17254147}, {63954, 17588739}, {63955, 17588995}, + {63956, 17589251}, {63957, 17589507}, {63958, 17589763}, {63959, 17590019}, + {63960, 17590275}, {63961, 17590531}, {63962, 17590787}, {63963, 17562115}, + {63964, 17591043}, {63965, 17591299}, {63966, 17591555}, {63967, 17591811}, + {63968, 17592067}, {63969, 17592323}, {63970, 17592579}, {63971, 17592835}, + {63972, 17593091}, {63973, 17593347}, {63974, 17593603}, {63975, 17593859}, + {63976, 17594115}, {63977, 17183747}, {63978, 17594371}, {63979, 17594627}, + {63980, 17594883}, {63981, 17595139}, {63982, 17595395}, {63983, 17595651}, + {63984, 17595907}, {63985, 17596163}, {63986, 17596419}, {63987, 17596675}, + {63988, 17596931}, {63989, 17597187}, {63990, 17597443}, {63991, 17171203}, + {63992, 17597699}, {63993, 17597955}, {63994, 17598211}, {63995, 17598467}, + {63996, 17598723}, {63997, 17598979}, {63998, 17599235}, {63999, 17599491}, + {64000, 17599747}, {64001, 17600003}, {64002, 17600259}, {64003, 17600515}, + {64004, 17600771}, {64005, 17601027}, {64006, 17601283}, {64007, 17601539}, + {64008, 17178115}, {64009, 17601795}, {64010, 17178883}, {64011, 17602051}, + {64012, 17602307}, {64013, 17602563}, {64014, 1}, {64016, 17602819}, + {64017, 1}, {64018, 17603075}, {64019, 1}, {64021, 17603331}, + {64022, 17603587}, {64023, 17603843}, {64024, 17604099}, {64025, 17604355}, + {64026, 17604611}, {64027, 17604867}, {64028, 17605123}, {64029, 17605379}, + {64030, 17172995}, {64031, 1}, {64032, 17605635}, {64033, 1}, + {64034, 17605891}, {64035, 1}, {64037, 17606147}, {64038, 17606403}, + {64039, 1}, {64042, 17606659}, {64043, 17606915}, {64044, 17607171}, + {64045, 17607427}, {64046, 17607683}, {64047, 17607939}, {64048, 17608195}, + {64049, 17608451}, {64050, 17608707}, {64051, 17608963}, {64052, 17609219}, + {64053, 17609475}, {64054, 17609731}, {64055, 17609987}, {64056, 17610243}, + {64057, 17610499}, {64058, 17610755}, {64059, 17611011}, {64060, 17152771}, + {64061, 17611267}, {64062, 17611523}, {64063, 17611779}, {64064, 17612035}, + {64065, 17612291}, {64066, 17612547}, {64067, 17612803}, {64068, 17613059}, + {64069, 17613315}, {64070, 17613571}, {64071, 17613827}, {64072, 17614083}, + {64073, 17614339}, {64074, 17614595}, {64075, 17614851}, {64076, 17264899}, + {64077, 17615107}, {64078, 17615363}, {64079, 17615619}, {64080, 17615875}, + {64081, 17267971}, {64082, 17616131}, {64083, 17616387}, {64084, 17616643}, + {64085, 17616899}, {64086, 17617155}, {64087, 17574915}, {64088, 17617411}, + {64089, 17617667}, {64090, 17617923}, {64091, 17618179}, {64092, 17618435}, + {64093, 17618691}, {64095, 17618947}, {64096, 17619203}, {64097, 17619459}, + {64098, 17619715}, {64099, 17619971}, {64100, 17620227}, {64101, 17620483}, + {64102, 17620739}, {64103, 17606147}, {64104, 17620995}, {64105, 17621251}, + {64106, 17621507}, {64107, 17621763}, {64108, 17622019}, {64109, 17622275}, + {64110, 2}, {64112, 17622531}, {64113, 17622787}, {64114, 17623043}, + {64115, 17623299}, {64116, 17623555}, {64117, 17623811}, {64118, 17624067}, + {64119, 17624323}, {64120, 17609731}, {64121, 17624579}, {64122, 17624835}, + {64123, 17625091}, {64124, 17602819}, {64125, 17625347}, {64126, 17625603}, + {64127, 17625859}, {64128, 17626115}, {64129, 17626371}, {64130, 17626627}, + {64131, 17626883}, {64132, 17627139}, {64133, 17627395}, {64134, 17627651}, + {64135, 17627907}, {64136, 17628163}, {64137, 17611779}, {64138, 17628419}, + {64139, 17612035}, {64140, 17628675}, {64141, 17628931}, {64142, 17629187}, + {64143, 17629443}, {64144, 17629699}, {64145, 17603075}, {64146, 17548547}, + {64147, 17629955}, {64148, 17630211}, {64149, 17161219}, {64150, 17565955}, + {64151, 17586947}, {64152, 17630467}, {64153, 17630723}, {64154, 17613827}, + {64155, 17630979}, {64156, 17614083}, {64157, 17631235}, {64158, 17631491}, + {64159, 17631747}, {64160, 17603587}, {64161, 17632003}, {64162, 17632259}, + {64163, 17632515}, {64164, 17632771}, {64165, 17633027}, {64166, 17603843}, + {64167, 17633283}, {64168, 17633539}, {64169, 17633795}, {64170, 17634051}, + {64171, 17634307}, {64172, 17634563}, {64173, 17617155}, {64174, 17634819}, + {64175, 17635075}, {64176, 17574915}, {64177, 17635331}, {64178, 17618179}, + {64179, 17635587}, {64180, 17635843}, {64181, 17636099}, {64182, 17636355}, + {64183, 17636611}, {64184, 17619459}, {64185, 17636867}, {64186, 17605891}, + {64187, 17637123}, {64188, 17619715}, {64189, 17561091}, {64190, 17637379}, + {64191, 17619971}, {64192, 17637635}, {64193, 17620483}, {64194, 17637891}, + {64195, 17638147}, {64196, 17638403}, {64197, 17638659}, {64198, 17638915}, + {64199, 17620995}, {64200, 17605123}, {64201, 17639171}, {64202, 17621251}, + {64203, 17639427}, {64204, 17621507}, {64205, 17639683}, {64206, 17195779}, + {64207, 17639939}, {64208, 17640195}, {64209, 17640451}, {64210, 17640707}, + {64211, 17640963}, {64212, 17641219}, {64213, 17641475}, {64214, 17641731}, + {64215, 17641987}, {64216, 17642243}, {64217, 17642499}, {64218, 2}, + {64256, 34419971}, {64257, 34420483}, {64258, 34420995}, {64259, 51197443}, + {64260, 51198723}, {64261, 33559043}, {64263, 2}, {64275, 34422275}, + {64276, 34422787}, {64277, 34423299}, {64278, 34423811}, {64279, 34424323}, + {64280, 2}, {64285, 34424835}, {64286, 1}, {64287, 34425347}, + {64288, 17648643}, {64289, 17043971}, {64290, 17044739}, {64291, 17648899}, + {64292, 17649155}, {64293, 17649411}, {64294, 17649667}, {64295, 17649923}, + {64296, 17650179}, {64297, 17036803}, {64298, 34427651}, {64299, 34428163}, + {64300, 51205891}, {64301, 51206659}, {64302, 34430211}, {64303, 34430723}, + {64304, 34431235}, {64305, 34431747}, {64306, 34432259}, {64307, 34432771}, + {64308, 34433283}, {64309, 34433795}, {64310, 34434307}, {64311, 2}, + {64312, 34434819}, {64313, 34435331}, {64314, 34435843}, {64315, 34436355}, + {64316, 34436867}, {64317, 2}, {64318, 34437379}, {64319, 2}, + {64320, 34437891}, {64321, 34438403}, {64322, 2}, {64323, 34438915}, + {64324, 34439427}, {64325, 2}, {64326, 34439939}, {64327, 34440451}, + {64328, 34440963}, {64329, 34428675}, {64330, 34441475}, {64331, 34441987}, + {64332, 34442499}, {64333, 34443011}, {64334, 34443523}, {64335, 34444035}, + {64336, 17667331}, {64338, 17667587}, {64342, 17667843}, {64346, 17668099}, + {64350, 17668355}, {64354, 17668611}, {64358, 17668867}, {64362, 17669123}, + {64366, 17669379}, {64370, 17669635}, {64374, 17669891}, {64378, 17670147}, + {64382, 17670403}, {64386, 17670659}, {64388, 17670915}, {64390, 17671171}, + {64392, 17671427}, {64394, 17671683}, {64396, 17671939}, {64398, 17672195}, + {64402, 17672451}, {64406, 17672707}, {64410, 17672963}, {64414, 17673219}, + {64416, 17673475}, {64420, 17673731}, {64422, 17673987}, {64426, 17674243}, + {64430, 17674499}, {64432, 17674755}, {64434, 1}, {64451, 2}, + {64467, 17675011}, {64471, 16911363}, {64473, 17675267}, {64475, 17675523}, + {64477, 33688579}, {64478, 17675779}, {64480, 17676035}, {64482, 17676291}, + {64484, 17676547}, {64488, 17676803}, {64490, 34454275}, {64492, 34454787}, + {64494, 34455299}, {64496, 34455811}, {64498, 34456323}, {64500, 34456835}, + {64502, 34457347}, {64505, 34457859}, {64508, 17681155}, {64512, 34458627}, + {64513, 34459139}, {64514, 34459651}, {64515, 34457859}, {64516, 34460163}, + {64517, 34460675}, {64518, 34461187}, {64519, 34461699}, {64520, 34462211}, + {64521, 34462723}, {64522, 34463235}, {64523, 34463747}, {64524, 34464259}, + {64525, 34464771}, {64526, 34465283}, {64527, 34465795}, {64528, 34466307}, + {64529, 34466819}, {64530, 34467331}, {64531, 34467843}, {64532, 34468355}, + {64533, 34468867}, {64534, 34469379}, {64535, 34469123}, {64536, 34469891}, + {64537, 34470403}, {64538, 34470915}, {64539, 34471427}, {64540, 34471939}, + {64541, 34472451}, {64542, 34472963}, {64543, 34473475}, {64544, 34473987}, + {64545, 34474499}, {64546, 34475011}, {64547, 34475523}, {64548, 34476035}, + {64549, 34476547}, {64550, 34477059}, {64551, 34477571}, {64552, 34478083}, + {64553, 34478595}, {64554, 34479107}, {64555, 34479619}, {64556, 34480131}, + {64557, 34480643}, {64558, 34481155}, {64559, 34481667}, {64560, 34482179}, + {64561, 34482691}, {64562, 34483203}, {64563, 34483715}, {64564, 34484227}, + {64565, 34484739}, {64566, 34485251}, {64567, 34485763}, {64568, 34486275}, + {64569, 34486787}, {64570, 34487299}, {64571, 34487811}, {64572, 34488323}, + {64573, 34488835}, {64574, 34489347}, {64575, 34489859}, {64576, 34490371}, + {64577, 34490883}, {64578, 34491395}, {64579, 34491907}, {64580, 34492419}, + {64581, 34492931}, {64582, 34469635}, {64583, 34470147}, {64584, 34493443}, + {64585, 34493955}, {64586, 34494467}, {64587, 34494979}, {64588, 34495491}, + {64589, 34496003}, {64590, 34496515}, {64591, 34497027}, {64592, 34497539}, + {64593, 34498051}, {64594, 34498563}, {64595, 34499075}, {64596, 34499587}, + {64597, 34468611}, {64598, 34500099}, {64599, 34500611}, {64600, 34492675}, + {64601, 34501123}, {64602, 34499843}, {64603, 34501635}, {64604, 34502147}, + {64605, 34502659}, {64606, 51280387}, {64607, 51281155}, {64608, 51281923}, + {64609, 51282691}, {64610, 51283459}, {64611, 51284227}, {64612, 34507779}, + {64613, 34508291}, {64614, 34459651}, {64615, 34508803}, {64616, 34457859}, + {64617, 34460163}, {64618, 34509315}, {64619, 34509827}, {64620, 34462211}, + {64621, 34510339}, {64622, 34462723}, {64623, 34463235}, {64624, 34510851}, + {64625, 34511363}, {64626, 34465283}, {64627, 34511875}, {64628, 34465795}, + {64629, 34466307}, {64630, 34512387}, {64631, 34512899}, {64632, 34467331}, + {64633, 34513411}, {64634, 34467843}, {64635, 34468355}, {64636, 34482691}, + {64637, 34483203}, {64638, 34484739}, {64639, 34485251}, {64640, 34485763}, + {64641, 34487811}, {64642, 34488323}, {64643, 34488835}, {64644, 34489347}, + {64645, 34491395}, {64646, 34491907}, {64647, 34492419}, {64648, 34513923}, + {64649, 34493443}, {64650, 34514435}, {64651, 34514947}, {64652, 34496515}, + {64653, 34515459}, {64654, 34497027}, {64655, 34497539}, {64656, 34502659}, + {64657, 34515971}, {64658, 34516483}, {64659, 34492675}, {64660, 34494723}, + {64661, 34501123}, {64662, 34499843}, {64663, 34458627}, {64664, 34459139}, + {64665, 34516995}, {64666, 34459651}, {64667, 34517507}, {64668, 34460675}, + {64669, 34461187}, {64670, 34461699}, {64671, 34462211}, {64672, 34518019}, + {64673, 34463747}, {64674, 34464259}, {64675, 34464771}, {64676, 34465283}, + {64677, 34518531}, {64678, 34467331}, {64679, 34468867}, {64680, 34469379}, + {64681, 34469123}, {64682, 34469891}, {64683, 34470403}, {64684, 34471427}, + {64685, 34471939}, {64686, 34472451}, {64687, 34472963}, {64688, 34473475}, + {64689, 34473987}, {64690, 34519043}, {64691, 34474499}, {64692, 34475011}, + {64693, 34475523}, {64694, 34476035}, {64695, 34476547}, {64696, 34477059}, + {64697, 34478083}, {64698, 34478595}, {64699, 34479107}, {64700, 34479619}, + {64701, 34480131}, {64702, 34480643}, {64703, 34481155}, {64704, 34481667}, + {64705, 34482179}, {64706, 34483715}, {64707, 34484227}, {64708, 34486275}, + {64709, 34486787}, {64710, 34487299}, {64711, 34487811}, {64712, 34488323}, + {64713, 34489859}, {64714, 34490371}, {64715, 34490883}, {64716, 34491395}, + {64717, 34519555}, {64718, 34492931}, {64719, 34469635}, {64720, 34470147}, + {64721, 34493443}, {64722, 34494979}, {64723, 34495491}, {64724, 34496003}, + {64725, 34496515}, {64726, 34520067}, {64727, 34498051}, {64728, 34498563}, + {64729, 34520579}, {64730, 34468611}, {64731, 34500099}, {64732, 34500611}, + {64733, 34492675}, {64734, 34497795}, {64735, 34459651}, {64736, 34517507}, + {64737, 34462211}, {64738, 34518019}, {64739, 34465283}, {64740, 34518531}, + {64741, 34467331}, {64742, 34521091}, {64743, 34473475}, {64744, 34521603}, + {64745, 34522115}, {64746, 34522627}, {64747, 34487811}, {64748, 34488323}, + {64749, 34491395}, {64750, 34496515}, {64751, 34520067}, {64752, 34492675}, + {64753, 34497795}, {64754, 51300355}, {64755, 51301123}, {64756, 51301891}, + {64757, 34525443}, {64758, 34525955}, {64759, 34526467}, {64760, 34526979}, + {64761, 34527491}, {64762, 34528003}, {64763, 34528515}, {64764, 34529027}, + {64765, 34529539}, {64766, 34530051}, {64767, 34530563}, {64768, 34500355}, + {64769, 34531075}, {64770, 34531587}, {64771, 34532099}, {64772, 34500867}, + {64773, 34532611}, {64774, 34533123}, {64775, 34533635}, {64776, 34534147}, + {64777, 34534659}, {64778, 34535171}, {64779, 34535683}, {64780, 34522115}, + {64781, 34536195}, {64782, 34536707}, {64783, 34537219}, {64784, 34537731}, + {64785, 34525443}, {64786, 34525955}, {64787, 34526467}, {64788, 34526979}, + {64789, 34527491}, {64790, 34528003}, {64791, 34528515}, {64792, 34529027}, + {64793, 34529539}, {64794, 34530051}, {64795, 34530563}, {64796, 34500355}, + {64797, 34531075}, {64798, 34531587}, {64799, 34532099}, {64800, 34500867}, + {64801, 34532611}, {64802, 34533123}, {64803, 34533635}, {64804, 34534147}, + {64805, 34534659}, {64806, 34535171}, {64807, 34535683}, {64808, 34522115}, + {64809, 34536195}, {64810, 34536707}, {64811, 34537219}, {64812, 34537731}, + {64813, 34534659}, {64814, 34535171}, {64815, 34535683}, {64816, 34522115}, + {64817, 34521603}, {64818, 34522627}, {64819, 34477571}, {64820, 34471939}, + {64821, 34472451}, {64822, 34472963}, {64823, 34534659}, {64824, 34535171}, + {64825, 34535683}, {64826, 34477571}, {64827, 34478083}, {64828, 34538243}, + {64830, 1}, {64848, 51315971}, {64849, 51316739}, {64851, 51317507}, + {64852, 51318275}, {64853, 51319043}, {64854, 51319811}, {64855, 51320579}, + {64856, 51246595}, {64858, 51321347}, {64859, 51322115}, {64860, 51322883}, + {64861, 51323651}, {64862, 51324419}, {64863, 51325187}, {64865, 51325955}, + {64866, 51326723}, {64868, 51327491}, {64870, 51328259}, {64871, 51329027}, + {64873, 51329795}, {64874, 51330563}, {64876, 51331331}, {64878, 51332099}, + {64879, 51332867}, {64881, 51333635}, {64883, 51334403}, {64884, 51335171}, + {64885, 51335939}, {64886, 51336707}, {64888, 51337475}, {64889, 51338243}, + {64890, 51339011}, {64891, 51339779}, {64892, 51340547}, {64894, 51341315}, + {64895, 51342083}, {64896, 51342851}, {64897, 51343619}, {64898, 51344387}, + {64899, 51345155}, {64901, 51345923}, {64903, 51346691}, {64905, 51347459}, + {64906, 51246851}, {64907, 51348227}, {64908, 51348995}, {64909, 51270147}, + {64910, 51247363}, {64911, 51349763}, {64912, 2}, {64914, 51350531}, + {64915, 51351299}, {64916, 51352067}, {64917, 51352835}, {64918, 51353603}, + {64919, 51354371}, {64921, 51355139}, {64922, 51355907}, {64923, 51356675}, + {64924, 51357443}, {64926, 51358211}, {64927, 51358979}, {64928, 51359747}, + {64929, 51360515}, {64930, 51361283}, {64931, 51362051}, {64932, 51362819}, + {64933, 51363587}, {64934, 51364355}, {64935, 51365123}, {64936, 51365891}, + {64937, 51366659}, {64938, 51367427}, {64939, 51368195}, {64940, 51368963}, + {64941, 51369731}, {64942, 51277315}, {64943, 51370499}, {64944, 51371267}, + {64945, 51372035}, {64946, 51372803}, {64947, 51373571}, {64948, 51341315}, + {64949, 51342851}, {64950, 51374339}, {64951, 51375107}, {64952, 51375875}, + {64953, 51376643}, {64954, 51377411}, {64955, 51378179}, {64956, 51377411}, + {64957, 51375875}, {64958, 51378947}, {64959, 51379715}, {64960, 51380483}, + {64961, 51381251}, {64962, 51382019}, {64963, 51378179}, {64964, 51335939}, + {64965, 51328259}, {64966, 51382787}, {64967, 51383555}, {64968, 2}, + {64975, 1}, {64976, 2}, {65008, 51384323}, {65009, 51385091}, + {65010, 68163075}, {65011, 68164099}, {65012, 68165123}, {65013, 68166147}, + {65014, 68167171}, {65015, 68168195}, {65016, 68169219}, {65017, 51393027}, + {65018, 303052035}, {65019, 135284483}, {65020, 68177667}, {65021, 1}, + {65024, 0}, {65040, 17847043}, {65041, 17847299}, {65042, 2}, + {65043, 17110275}, {65044, 16848643}, {65045, 17032707}, {65046, 17033731}, + {65047, 17847555}, {65048, 17847811}, {65049, 2}, {65056, 1}, + {65072, 2}, {65073, 17848067}, {65074, 17848323}, {65075, 17848579}, + {65077, 17037571}, {65078, 17037827}, {65079, 17848835}, {65080, 17849091}, + {65081, 17849347}, {65082, 17849603}, {65083, 17849859}, {65084, 17850115}, + {65085, 17850371}, {65086, 17850627}, {65087, 17067011}, {65088, 17067267}, + {65089, 17850883}, {65090, 17851139}, {65091, 17851395}, {65092, 17851651}, + {65093, 1}, {65095, 17851907}, {65096, 17852163}, {65097, 33810435}, + {65101, 17848579}, {65104, 17847043}, {65105, 17847299}, {65106, 2}, + {65108, 16848643}, {65109, 17110275}, {65110, 17033731}, {65111, 17032707}, + {65112, 17848067}, {65113, 17037571}, {65114, 17037827}, {65115, 17848835}, + {65116, 17849091}, {65117, 17849347}, {65118, 17849603}, {65119, 17852419}, + {65120, 17852675}, {65121, 17852931}, {65122, 17036803}, {65123, 17853187}, + {65124, 17853443}, {65125, 17853699}, {65126, 17037315}, {65127, 2}, + {65128, 17853955}, {65129, 17854211}, {65130, 17854467}, {65131, 17854723}, + {65132, 2}, {65136, 34632195}, {65137, 34632707}, {65138, 34503171}, + {65139, 1}, {65140, 34503939}, {65141, 2}, {65142, 34504707}, + {65143, 34523139}, {65144, 34505475}, {65145, 34523907}, {65146, 34506243}, + {65147, 34524675}, {65148, 34507011}, {65149, 34633219}, {65150, 34633731}, + {65151, 34634243}, {65152, 17857539}, {65153, 17857795}, {65155, 17858051}, + {65157, 17858307}, {65159, 17858563}, {65161, 17677059}, {65165, 16910339}, + {65167, 17683459}, {65171, 17858819}, {65173, 17686531}, {65177, 17689603}, + {65181, 17681667}, {65185, 17682179}, {65189, 17684739}, {65193, 17834243}, + {65195, 17724419}, {65197, 17724931}, {65199, 17731331}, {65201, 17694723}, + {65205, 17744899}, {65209, 17696771}, {65213, 17697795}, {65217, 17699843}, + {65221, 17700867}, {65225, 17701379}, {65229, 17702403}, {65233, 17703427}, + {65237, 17706499}, {65241, 17708547}, {65245, 17710851}, {65249, 17682691}, + {65253, 17717763}, {65257, 17720835}, {65261, 16910851}, {65263, 17676803}, + {65265, 16911875}, {65269, 34636291}, {65271, 34636803}, {65273, 34637315}, + {65275, 34622211}, {65277, 2}, {65279, 0}, {65280, 2}, + {65281, 17032707}, {65282, 17860611}, {65283, 17852419}, {65284, 17854211}, + {65285, 17854467}, {65286, 17852675}, {65287, 17860867}, {65288, 17037571}, + {65289, 17037827}, {65290, 17852931}, {65291, 17036803}, {65292, 17847043}, + {65293, 17853187}, {65294, 17196291}, {65295, 17038339}, {65296, 17035267}, {65297, 16786947}, {65298, 16785155}, {65299, 16785411}, {65300, 16787715}, - {65301, 17035779}, {65302, 17036035}, {65303, 17036291}, {65304, 17036547}, - {65305, 17036803}, {65306, 17110531}, {65307, 16848643}, {65308, 17853699}, - {65309, 17037571}, {65310, 17853955}, {65311, 17033987}, {65312, 17854979}, + {65301, 17035523}, {65302, 17035779}, {65303, 17036035}, {65304, 17036291}, + {65305, 17036547}, {65306, 17110275}, {65307, 16848643}, {65308, 17853443}, + {65309, 17037315}, {65310, 17853699}, {65311, 17033731}, {65312, 17854723}, {65313, 16777219}, {65314, 16777475}, {65315, 16777731}, {65316, 16777987}, {65317, 16778243}, {65318, 16778499}, {65319, 16778755}, {65320, 16779011}, {65321, 16779267}, {65322, 16779523}, {65323, 16779779}, {65324, 16780035}, {65325, 16780291}, {65326, 16780547}, {65327, 16780803}, {65328, 16781059}, {65329, 16781315}, {65330, 16781571}, {65331, 16781827}, {65332, 16782083}, {65333, 16782339}, {65334, 16782595}, {65335, 16782851}, {65336, 16783107}, - {65337, 16783363}, {65338, 16783619}, {65339, 17852163}, {65340, 17854211}, - {65341, 17852419}, {65342, 17861379}, {65343, 17848835}, {65344, 17027075}, + {65337, 16783363}, {65338, 16783619}, {65339, 17851907}, {65340, 17853955}, + {65341, 17852163}, {65342, 17861123}, {65343, 17848579}, {65344, 17026819}, {65345, 16777219}, {65346, 16777475}, {65347, 16777731}, {65348, 16777987}, {65349, 16778243}, {65350, 16778499}, {65351, 16778755}, {65352, 16779011}, {65353, 16779267}, {65354, 16779523}, {65355, 16779779}, {65356, 16780035}, {65357, 16780291}, {65358, 16780547}, {65359, 16780803}, {65360, 16781059}, {65361, 16781315}, {65362, 16781571}, {65363, 16781827}, {65364, 16782083}, {65365, 16782339}, {65366, 16782595}, {65367, 16782851}, {65368, 16783107}, - {65369, 16783363}, {65370, 16783619}, {65371, 17849091}, {65372, 17861635}, - {65373, 17849347}, {65374, 17861891}, {65375, 17862147}, {65376, 17862403}, - {65377, 17196547}, {65378, 17851139}, {65379, 17851395}, {65380, 17847555}, - {65381, 17862659}, {65382, 17316867}, {65383, 17319427}, {65384, 17362435}, - {65385, 17862915}, {65386, 17363971}, {65387, 17323523}, {65388, 17863171}, - {65389, 17333763}, {65390, 17379587}, {65391, 17329155}, {65392, 17318147}, - {65393, 17305603}, {65394, 17305859}, {65395, 17306115}, {65396, 17306371}, - {65397, 17306627}, {65398, 17306883}, {65399, 17307139}, {65400, 17307395}, - {65401, 17307651}, {65402, 17199107}, {65403, 17307907}, {65404, 17308163}, - {65405, 17308419}, {65406, 17308675}, {65407, 17308931}, {65408, 17309187}, - {65409, 17309443}, {65410, 17309699}, {65411, 17309955}, {65412, 17199363}, - {65413, 17310211}, {65414, 17310467}, {65415, 17310723}, {65416, 17310979}, - {65417, 17311235}, {65418, 17311491}, {65419, 17311747}, {65420, 17312003}, - {65421, 17312259}, {65422, 17312515}, {65423, 17312771}, {65424, 17313027}, - {65425, 17313283}, {65426, 17313539}, {65427, 17313795}, {65428, 17314051}, - {65429, 17314307}, {65430, 17314563}, {65431, 17314819}, {65432, 17315075}, - {65433, 17315331}, {65434, 17315587}, {65435, 17315843}, {65436, 17316099}, - {65437, 17319939}, {65438, 17197827}, {65439, 17198339}, {65440, 2}, - {65441, 17199619}, {65442, 17199875}, {65443, 17200131}, {65444, 17200387}, - {65445, 17200643}, {65446, 17200899}, {65447, 17201155}, {65448, 17201411}, - {65449, 17201667}, {65450, 17201923}, {65451, 17202179}, {65452, 17202435}, - {65453, 17202691}, {65454, 17202947}, {65455, 17203203}, {65456, 17203459}, - {65457, 17203715}, {65458, 17203971}, {65459, 17204227}, {65460, 17204483}, - {65461, 17204739}, {65462, 17204995}, {65463, 17205251}, {65464, 17205507}, - {65465, 17205763}, {65466, 17206019}, {65467, 17206275}, {65468, 17206531}, - {65469, 17206787}, {65470, 17207043}, {65471, 2}, {65474, 17207299}, - {65475, 17207555}, {65476, 17207811}, {65477, 17208067}, {65478, 17208323}, - {65479, 17208579}, {65480, 2}, {65482, 17208835}, {65483, 17209091}, - {65484, 17209347}, {65485, 17209603}, {65486, 17209859}, {65487, 17210115}, - {65488, 2}, {65490, 17210371}, {65491, 17210627}, {65492, 17210883}, - {65493, 17211139}, {65494, 17211395}, {65495, 17211651}, {65496, 2}, - {65498, 17211907}, {65499, 17212163}, {65500, 17212419}, {65501, 2}, - {65504, 17863427}, {65505, 17863683}, {65506, 17863939}, {65507, 33561859}, - {65508, 17864195}, {65509, 17864451}, {65510, 17864707}, {65511, 2}, - {65512, 17864963}, {65513, 17865219}, {65514, 17865475}, {65515, 17865731}, - {65516, 17865987}, {65517, 17866243}, {65518, 17866499}, {65519, 2}, + {65369, 16783363}, {65370, 16783619}, {65371, 17848835}, {65372, 17861379}, + {65373, 17849091}, {65374, 17861635}, {65375, 17861891}, {65376, 17862147}, + {65377, 17196291}, {65378, 17850883}, {65379, 17851139}, {65380, 17847299}, + {65381, 17862403}, {65382, 17316611}, {65383, 17319171}, {65384, 17362179}, + {65385, 17862659}, {65386, 17363715}, {65387, 17323267}, {65388, 17862915}, + {65389, 17333507}, {65390, 17379331}, {65391, 17328899}, {65392, 17317891}, + {65393, 17305347}, {65394, 17305603}, {65395, 17305859}, {65396, 17306115}, + {65397, 17306371}, {65398, 17306627}, {65399, 17306883}, {65400, 17307139}, + {65401, 17307395}, {65402, 17198851}, {65403, 17307651}, {65404, 17307907}, + {65405, 17308163}, {65406, 17308419}, {65407, 17308675}, {65408, 17308931}, + {65409, 17309187}, {65410, 17309443}, {65411, 17309699}, {65412, 17199107}, + {65413, 17309955}, {65414, 17310211}, {65415, 17310467}, {65416, 17310723}, + {65417, 17310979}, {65418, 17311235}, {65419, 17311491}, {65420, 17311747}, + {65421, 17312003}, {65422, 17312259}, {65423, 17312515}, {65424, 17312771}, + {65425, 17313027}, {65426, 17313283}, {65427, 17313539}, {65428, 17313795}, + {65429, 17314051}, {65430, 17314307}, {65431, 17314563}, {65432, 17314819}, + {65433, 17315075}, {65434, 17315331}, {65435, 17315587}, {65436, 17315843}, + {65437, 17319683}, {65438, 17197571}, {65439, 17198083}, {65440, 2}, + {65441, 17199363}, {65442, 17199619}, {65443, 17199875}, {65444, 17200131}, + {65445, 17200387}, {65446, 17200643}, {65447, 17200899}, {65448, 17201155}, + {65449, 17201411}, {65450, 17201667}, {65451, 17201923}, {65452, 17202179}, + {65453, 17202435}, {65454, 17202691}, {65455, 17202947}, {65456, 17203203}, + {65457, 17203459}, {65458, 17203715}, {65459, 17203971}, {65460, 17204227}, + {65461, 17204483}, {65462, 17204739}, {65463, 17204995}, {65464, 17205251}, + {65465, 17205507}, {65466, 17205763}, {65467, 17206019}, {65468, 17206275}, + {65469, 17206531}, {65470, 17206787}, {65471, 2}, {65474, 17207043}, + {65475, 17207299}, {65476, 17207555}, {65477, 17207811}, {65478, 17208067}, + {65479, 17208323}, {65480, 2}, {65482, 17208579}, {65483, 17208835}, + {65484, 17209091}, {65485, 17209347}, {65486, 17209603}, {65487, 17209859}, + {65488, 2}, {65490, 17210115}, {65491, 17210371}, {65492, 17210627}, + {65493, 17210883}, {65494, 17211139}, {65495, 17211395}, {65496, 2}, + {65498, 17211651}, {65499, 17211907}, {65500, 17212163}, {65501, 2}, + {65504, 17863171}, {65505, 17863427}, {65506, 17863683}, {65507, 33561859}, + {65508, 17863939}, {65509, 17864195}, {65510, 17864451}, {65511, 2}, + {65512, 17864707}, {65513, 17864963}, {65514, 17865219}, {65515, 17865475}, + {65516, 17865731}, {65517, 17865987}, {65518, 17866243}, {65519, 2}, {65536, 1}, {65548, 2}, {65549, 1}, {65575, 2}, {65576, 1}, {65595, 2}, {65596, 1}, {65598, 2}, {65599, 1}, {65614, 2}, {65616, 1}, {65630, 2}, @@ -2011,56 +2012,56 @@ const uint32_t table[8000][2] = {66304, 1}, {66340, 2}, {66349, 1}, {66379, 2}, {66384, 1}, {66427, 2}, {66432, 1}, {66462, 2}, {66463, 1}, {66500, 2}, {66504, 1}, {66518, 2}, - {66560, 17866755}, {66561, 17867011}, {66562, 17867267}, {66563, 17867523}, - {66564, 17867779}, {66565, 17868035}, {66566, 17868291}, {66567, 17868547}, - {66568, 17868803}, {66569, 17869059}, {66570, 17869315}, {66571, 17869571}, - {66572, 17869827}, {66573, 17870083}, {66574, 17870339}, {66575, 17870595}, - {66576, 17870851}, {66577, 17871107}, {66578, 17871363}, {66579, 17871619}, - {66580, 17871875}, {66581, 17872131}, {66582, 17872387}, {66583, 17872643}, - {66584, 17872899}, {66585, 17873155}, {66586, 17873411}, {66587, 17873667}, - {66588, 17873923}, {66589, 17874179}, {66590, 17874435}, {66591, 17874691}, - {66592, 17874947}, {66593, 17875203}, {66594, 17875459}, {66595, 17875715}, - {66596, 17875971}, {66597, 17876227}, {66598, 17876483}, {66599, 17876739}, + {66560, 17866499}, {66561, 17866755}, {66562, 17867011}, {66563, 17867267}, + {66564, 17867523}, {66565, 17867779}, {66566, 17868035}, {66567, 17868291}, + {66568, 17868547}, {66569, 17868803}, {66570, 17869059}, {66571, 17869315}, + {66572, 17869571}, {66573, 17869827}, {66574, 17870083}, {66575, 17870339}, + {66576, 17870595}, {66577, 17870851}, {66578, 17871107}, {66579, 17871363}, + {66580, 17871619}, {66581, 17871875}, {66582, 17872131}, {66583, 17872387}, + {66584, 17872643}, {66585, 17872899}, {66586, 17873155}, {66587, 17873411}, + {66588, 17873667}, {66589, 17873923}, {66590, 17874179}, {66591, 17874435}, + {66592, 17874691}, {66593, 17874947}, {66594, 17875203}, {66595, 17875459}, + {66596, 17875715}, {66597, 17875971}, {66598, 17876227}, {66599, 17876483}, {66600, 1}, {66718, 2}, {66720, 1}, {66730, 2}, - {66736, 17876995}, {66737, 17877251}, {66738, 17877507}, {66739, 17877763}, - {66740, 17878019}, {66741, 17878275}, {66742, 17878531}, {66743, 17878787}, - {66744, 17879043}, {66745, 17879299}, {66746, 17879555}, {66747, 17879811}, - {66748, 17880067}, {66749, 17880323}, {66750, 17880579}, {66751, 17880835}, - {66752, 17881091}, {66753, 17881347}, {66754, 17881603}, {66755, 17881859}, - {66756, 17882115}, {66757, 17882371}, {66758, 17882627}, {66759, 17882883}, - {66760, 17883139}, {66761, 17883395}, {66762, 17883651}, {66763, 17883907}, - {66764, 17884163}, {66765, 17884419}, {66766, 17884675}, {66767, 17884931}, - {66768, 17885187}, {66769, 17885443}, {66770, 17885699}, {66771, 17885955}, + {66736, 17876739}, {66737, 17876995}, {66738, 17877251}, {66739, 17877507}, + {66740, 17877763}, {66741, 17878019}, {66742, 17878275}, {66743, 17878531}, + {66744, 17878787}, {66745, 17879043}, {66746, 17879299}, {66747, 17879555}, + {66748, 17879811}, {66749, 17880067}, {66750, 17880323}, {66751, 17880579}, + {66752, 17880835}, {66753, 17881091}, {66754, 17881347}, {66755, 17881603}, + {66756, 17881859}, {66757, 17882115}, {66758, 17882371}, {66759, 17882627}, + {66760, 17882883}, {66761, 17883139}, {66762, 17883395}, {66763, 17883651}, + {66764, 17883907}, {66765, 17884163}, {66766, 17884419}, {66767, 17884675}, + {66768, 17884931}, {66769, 17885187}, {66770, 17885443}, {66771, 17885699}, {66772, 2}, {66776, 1}, {66812, 2}, {66816, 1}, {66856, 2}, {66864, 1}, {66916, 2}, {66927, 1}, - {66928, 17886211}, {66929, 17886467}, {66930, 17886723}, {66931, 17886979}, - {66932, 17887235}, {66933, 17887491}, {66934, 17887747}, {66935, 17888003}, - {66936, 17888259}, {66937, 17888515}, {66938, 17888771}, {66939, 2}, - {66940, 17889027}, {66941, 17889283}, {66942, 17889539}, {66943, 17889795}, - {66944, 17890051}, {66945, 17890307}, {66946, 17890563}, {66947, 17890819}, - {66948, 17891075}, {66949, 17891331}, {66950, 17891587}, {66951, 17891843}, - {66952, 17892099}, {66953, 17892355}, {66954, 17892611}, {66955, 2}, - {66956, 17892867}, {66957, 17893123}, {66958, 17893379}, {66959, 17893635}, - {66960, 17893891}, {66961, 17894147}, {66962, 17894403}, {66963, 2}, - {66964, 17894659}, {66965, 17894915}, {66966, 2}, {66967, 1}, + {66928, 17885955}, {66929, 17886211}, {66930, 17886467}, {66931, 17886723}, + {66932, 17886979}, {66933, 17887235}, {66934, 17887491}, {66935, 17887747}, + {66936, 17888003}, {66937, 17888259}, {66938, 17888515}, {66939, 2}, + {66940, 17888771}, {66941, 17889027}, {66942, 17889283}, {66943, 17889539}, + {66944, 17889795}, {66945, 17890051}, {66946, 17890307}, {66947, 17890563}, + {66948, 17890819}, {66949, 17891075}, {66950, 17891331}, {66951, 17891587}, + {66952, 17891843}, {66953, 17892099}, {66954, 17892355}, {66955, 2}, + {66956, 17892611}, {66957, 17892867}, {66958, 17893123}, {66959, 17893379}, + {66960, 17893635}, {66961, 17893891}, {66962, 17894147}, {66963, 2}, + {66964, 17894403}, {66965, 17894659}, {66966, 2}, {66967, 1}, {66978, 2}, {66979, 1}, {66994, 2}, {66995, 1}, {67002, 2}, {67003, 1}, {67005, 2}, {67072, 1}, {67383, 2}, {67392, 1}, {67414, 2}, {67424, 1}, - {67432, 2}, {67456, 1}, {67457, 17895171}, {67458, 17895427}, - {67459, 16791043}, {67460, 17895683}, {67461, 16814083}, {67462, 2}, - {67463, 17895939}, {67464, 17896195}, {67465, 17896451}, {67466, 17896707}, - {67467, 16815363}, {67468, 16815619}, {67469, 17896963}, {67470, 17897219}, - {67471, 17897475}, {67472, 17897731}, {67473, 17897987}, {67474, 17898243}, - {67475, 16817155}, {67476, 17898499}, {67477, 16802051}, {67478, 17898755}, - {67479, 17899011}, {67480, 17899267}, {67481, 17899523}, {67482, 17899779}, - {67483, 17512963}, {67484, 17900035}, {67485, 17900291}, {67486, 17900547}, - {67487, 17900803}, {67488, 17901059}, {67489, 17901315}, {67490, 16795395}, - {67491, 17901571}, {67492, 17901827}, {67493, 16781315}, {67494, 17902083}, - {67495, 17902339}, {67496, 17125379}, {67497, 17902595}, {67498, 16819971}, - {67499, 17902851}, {67500, 17903107}, {67501, 17903363}, {67502, 17903619}, - {67503, 16820995}, {67504, 17903875}, {67505, 2}, {67506, 17904131}, - {67507, 17904387}, {67508, 17904643}, {67509, 17904899}, {67510, 17905155}, - {67511, 17905411}, {67512, 17905667}, {67513, 17905923}, {67514, 17906179}, + {67432, 2}, {67456, 1}, {67457, 17894915}, {67458, 17895171}, + {67459, 16791043}, {67460, 17895427}, {67461, 16814083}, {67462, 2}, + {67463, 17895683}, {67464, 17895939}, {67465, 17896195}, {67466, 17896451}, + {67467, 16815363}, {67468, 16815619}, {67469, 17896707}, {67470, 17896963}, + {67471, 17897219}, {67472, 17897475}, {67473, 17897731}, {67474, 17897987}, + {67475, 16817155}, {67476, 17898243}, {67477, 16802051}, {67478, 17898499}, + {67479, 17898755}, {67480, 17899011}, {67481, 17899267}, {67482, 17899523}, + {67483, 17512707}, {67484, 17899779}, {67485, 17900035}, {67486, 17900291}, + {67487, 17900547}, {67488, 17900803}, {67489, 17901059}, {67490, 16795395}, + {67491, 17901315}, {67492, 17901571}, {67493, 16781315}, {67494, 17901827}, + {67495, 17902083}, {67496, 17125123}, {67497, 17902339}, {67498, 16819971}, + {67499, 17902595}, {67500, 17902851}, {67501, 17903107}, {67502, 17903363}, + {67503, 16820995}, {67504, 17903619}, {67505, 2}, {67506, 17903875}, + {67507, 17904131}, {67508, 17904387}, {67509, 17904643}, {67510, 17904899}, + {67511, 17905155}, {67512, 17905411}, {67513, 17905667}, {67514, 17905923}, {67515, 2}, {67584, 1}, {67590, 2}, {67592, 1}, {67593, 2}, {67594, 1}, {67638, 2}, {67639, 1}, {67641, 2}, {67644, 1}, {67645, 2}, {67647, 1}, @@ -2077,20 +2078,20 @@ const uint32_t table[8000][2] = {68343, 2}, {68352, 1}, {68406, 2}, {68409, 1}, {68438, 2}, {68440, 1}, {68467, 2}, {68472, 1}, {68498, 2}, {68505, 1}, {68509, 2}, {68521, 1}, - {68528, 2}, {68608, 1}, {68681, 2}, {68736, 17906435}, - {68737, 17906691}, {68738, 17906947}, {68739, 17907203}, {68740, 17907459}, - {68741, 17907715}, {68742, 17907971}, {68743, 17908227}, {68744, 17908483}, - {68745, 17908739}, {68746, 17908995}, {68747, 17909251}, {68748, 17909507}, - {68749, 17909763}, {68750, 17910019}, {68751, 17910275}, {68752, 17910531}, - {68753, 17910787}, {68754, 17911043}, {68755, 17911299}, {68756, 17911555}, - {68757, 17911811}, {68758, 17912067}, {68759, 17912323}, {68760, 17912579}, - {68761, 17912835}, {68762, 17913091}, {68763, 17913347}, {68764, 17913603}, - {68765, 17913859}, {68766, 17914115}, {68767, 17914371}, {68768, 17914627}, - {68769, 17914883}, {68770, 17915139}, {68771, 17915395}, {68772, 17915651}, - {68773, 17915907}, {68774, 17916163}, {68775, 17916419}, {68776, 17916675}, - {68777, 17916931}, {68778, 17917187}, {68779, 17917443}, {68780, 17917699}, - {68781, 17917955}, {68782, 17918211}, {68783, 17918467}, {68784, 17918723}, - {68785, 17918979}, {68786, 17919235}, {68787, 2}, {68800, 1}, + {68528, 2}, {68608, 1}, {68681, 2}, {68736, 17906179}, + {68737, 17906435}, {68738, 17906691}, {68739, 17906947}, {68740, 17907203}, + {68741, 17907459}, {68742, 17907715}, {68743, 17907971}, {68744, 17908227}, + {68745, 17908483}, {68746, 17908739}, {68747, 17908995}, {68748, 17909251}, + {68749, 17909507}, {68750, 17909763}, {68751, 17910019}, {68752, 17910275}, + {68753, 17910531}, {68754, 17910787}, {68755, 17911043}, {68756, 17911299}, + {68757, 17911555}, {68758, 17911811}, {68759, 17912067}, {68760, 17912323}, + {68761, 17912579}, {68762, 17912835}, {68763, 17913091}, {68764, 17913347}, + {68765, 17913603}, {68766, 17913859}, {68767, 17914115}, {68768, 17914371}, + {68769, 17914627}, {68770, 17914883}, {68771, 17915139}, {68772, 17915395}, + {68773, 17915651}, {68774, 17915907}, {68775, 17916163}, {68776, 17916419}, + {68777, 17916675}, {68778, 17916931}, {68779, 17917187}, {68780, 17917443}, + {68781, 17917699}, {68782, 17917955}, {68783, 17918211}, {68784, 17918467}, + {68785, 17918723}, {68786, 17918979}, {68787, 2}, {68800, 1}, {68851, 2}, {68858, 1}, {68904, 2}, {68912, 1}, {68922, 2}, {69216, 1}, {69247, 2}, {69248, 1}, {69290, 2}, {69291, 1}, {69294, 2}, {69296, 1}, @@ -2121,15 +2122,15 @@ const uint32_t table[8000][2] = {71258, 2}, {71264, 1}, {71277, 2}, {71296, 1}, {71354, 2}, {71360, 1}, {71370, 2}, {71424, 1}, {71451, 2}, {71453, 1}, {71468, 2}, {71472, 1}, - {71495, 2}, {71680, 1}, {71740, 2}, {71840, 17919491}, - {71841, 17919747}, {71842, 17920003}, {71843, 17920259}, {71844, 17920515}, - {71845, 17920771}, {71846, 17921027}, {71847, 17921283}, {71848, 17921539}, - {71849, 17921795}, {71850, 17922051}, {71851, 17922307}, {71852, 17922563}, - {71853, 17922819}, {71854, 17923075}, {71855, 17923331}, {71856, 17923587}, - {71857, 17923843}, {71858, 17924099}, {71859, 17924355}, {71860, 17924611}, - {71861, 17924867}, {71862, 17925123}, {71863, 17925379}, {71864, 17925635}, - {71865, 17925891}, {71866, 17926147}, {71867, 17926403}, {71868, 17926659}, - {71869, 17926915}, {71870, 17927171}, {71871, 17927427}, {71872, 1}, + {71495, 2}, {71680, 1}, {71740, 2}, {71840, 17919235}, + {71841, 17919491}, {71842, 17919747}, {71843, 17920003}, {71844, 17920259}, + {71845, 17920515}, {71846, 17920771}, {71847, 17921027}, {71848, 17921283}, + {71849, 17921539}, {71850, 17921795}, {71851, 17922051}, {71852, 17922307}, + {71853, 17922563}, {71854, 17922819}, {71855, 17923075}, {71856, 17923331}, + {71857, 17923587}, {71858, 17923843}, {71859, 17924099}, {71860, 17924355}, + {71861, 17924611}, {71862, 17924867}, {71863, 17925123}, {71864, 17925379}, + {71865, 17925635}, {71866, 17925891}, {71867, 17926147}, {71868, 17926403}, + {71869, 17926659}, {71870, 17926915}, {71871, 17927171}, {71872, 1}, {71923, 2}, {71935, 1}, {71943, 2}, {71945, 1}, {71946, 2}, {71948, 1}, {71956, 2}, {71957, 1}, {71959, 2}, {71960, 1}, {71990, 2}, {71991, 1}, @@ -2160,15 +2161,15 @@ const uint32_t table[8000][2] = {92874, 2}, {92880, 1}, {92910, 2}, {92912, 1}, {92918, 2}, {92928, 1}, {92998, 2}, {93008, 1}, {93018, 2}, {93019, 1}, {93026, 2}, {93027, 1}, - {93048, 2}, {93053, 1}, {93072, 2}, {93760, 17927683}, - {93761, 17927939}, {93762, 17928195}, {93763, 17928451}, {93764, 17928707}, - {93765, 17928963}, {93766, 17929219}, {93767, 17929475}, {93768, 17929731}, - {93769, 17929987}, {93770, 17930243}, {93771, 17930499}, {93772, 17930755}, - {93773, 17931011}, {93774, 17931267}, {93775, 17931523}, {93776, 17931779}, - {93777, 17932035}, {93778, 17932291}, {93779, 17932547}, {93780, 17932803}, - {93781, 17933059}, {93782, 17933315}, {93783, 17933571}, {93784, 17933827}, - {93785, 17934083}, {93786, 17934339}, {93787, 17934595}, {93788, 17934851}, - {93789, 17935107}, {93790, 17935363}, {93791, 17935619}, {93792, 1}, + {93048, 2}, {93053, 1}, {93072, 2}, {93760, 17927427}, + {93761, 17927683}, {93762, 17927939}, {93763, 17928195}, {93764, 17928451}, + {93765, 17928707}, {93766, 17928963}, {93767, 17929219}, {93768, 17929475}, + {93769, 17929731}, {93770, 17929987}, {93771, 17930243}, {93772, 17930499}, + {93773, 17930755}, {93774, 17931011}, {93775, 17931267}, {93776, 17931523}, + {93777, 17931779}, {93778, 17932035}, {93779, 17932291}, {93780, 17932547}, + {93781, 17932803}, {93782, 17933059}, {93783, 17933315}, {93784, 17933571}, + {93785, 17933827}, {93786, 17934083}, {93787, 17934339}, {93788, 17934595}, + {93789, 17934851}, {93790, 17935107}, {93791, 17935363}, {93792, 1}, {93851, 2}, {93952, 1}, {94027, 2}, {94031, 1}, {94088, 2}, {94095, 1}, {94112, 2}, {94176, 1}, {94181, 2}, {94192, 1}, {94194, 2}, {94208, 1}, @@ -2183,11 +2184,11 @@ const uint32_t table[8000][2] = {113824, 0}, {113828, 2}, {118528, 1}, {118574, 2}, {118576, 1}, {118599, 2}, {118608, 1}, {118724, 2}, {118784, 1}, {119030, 2}, {119040, 1}, {119079, 2}, - {119081, 1}, {119134, 34713091}, {119135, 34713603}, {119136, 51491331}, - {119137, 51492099}, {119138, 51492867}, {119139, 51493635}, {119140, 51494403}, - {119141, 1}, {119155, 2}, {119163, 1}, {119227, 34717955}, - {119228, 34718467}, {119229, 51496195}, {119230, 51496963}, {119231, 51497731}, - {119232, 51498499}, {119233, 1}, {119275, 2}, {119296, 1}, + {119081, 1}, {119134, 34712835}, {119135, 34713347}, {119136, 51491075}, + {119137, 51491843}, {119138, 51492611}, {119139, 51493379}, {119140, 51494147}, + {119141, 1}, {119155, 2}, {119163, 1}, {119227, 34717699}, + {119228, 34718211}, {119229, 51495939}, {119230, 51496707}, {119231, 51497475}, + {119232, 51498243}, {119233, 1}, {119275, 2}, {119296, 1}, {119366, 2}, {119488, 1}, {119508, 2}, {119520, 1}, {119540, 2}, {119552, 1}, {119639, 2}, {119648, 1}, {119673, 2}, {119808, 16777219}, {119809, 16777475}, {119810, 16777731}, @@ -2357,21 +2358,21 @@ const uint32_t table[8000][2] = {120469, 16780035}, {120470, 16780291}, {120471, 16780547}, {120472, 16780803}, {120473, 16781059}, {120474, 16781315}, {120475, 16781571}, {120476, 16781827}, {120477, 16782083}, {120478, 16782339}, {120479, 16782595}, {120480, 16782851}, - {120481, 16783107}, {120482, 16783363}, {120483, 16783619}, {120484, 17944835}, - {120485, 17945091}, {120486, 2}, {120488, 16851715}, {120489, 16851971}, + {120481, 16783107}, {120482, 16783363}, {120483, 16783619}, {120484, 17944579}, + {120485, 17944835}, {120486, 2}, {120488, 16851715}, {120489, 16851971}, {120490, 16852227}, {120491, 16852483}, {120492, 16852739}, {120493, 16852995}, {120494, 16853251}, {120495, 16853507}, {120496, 16846851}, {120497, 16853763}, {120498, 16854019}, {120499, 16786179}, {120500, 16854275}, {120501, 16854531}, {120502, 16854787}, {120503, 16855043}, {120504, 16855299}, {120505, 16853507}, {120506, 16855555}, {120507, 16855811}, {120508, 16856067}, {120509, 16856323}, - {120510, 16856579}, {120511, 16856835}, {120512, 16857091}, {120513, 17945347}, + {120510, 16856579}, {120511, 16856835}, {120512, 16857091}, {120513, 17945091}, {120514, 16851715}, {120515, 16851971}, {120516, 16852227}, {120517, 16852483}, {120518, 16852739}, {120519, 16852995}, {120520, 16853251}, {120521, 16853507}, {120522, 16846851}, {120523, 16853763}, {120524, 16854019}, {120525, 16786179}, {120526, 16854275}, {120527, 16854531}, {120528, 16854787}, {120529, 16855043}, {120530, 16855299}, {120531, 16855555}, {120533, 16855811}, {120534, 16856067}, {120535, 16856323}, {120536, 16856579}, {120537, 16856835}, {120538, 16857091}, - {120539, 17945603}, {120540, 16852739}, {120541, 16853507}, {120542, 16853763}, + {120539, 17945347}, {120540, 16852739}, {120541, 16853507}, {120542, 16853763}, {120543, 16856323}, {120544, 16855299}, {120545, 16855043}, {120546, 16851715}, {120547, 16851971}, {120548, 16852227}, {120549, 16852483}, {120550, 16852739}, {120551, 16852995}, {120552, 16853251}, {120553, 16853507}, {120554, 16846851}, @@ -2379,13 +2380,13 @@ const uint32_t table[8000][2] = {120559, 16854531}, {120560, 16854787}, {120561, 16855043}, {120562, 16855299}, {120563, 16853507}, {120564, 16855555}, {120565, 16855811}, {120566, 16856067}, {120567, 16856323}, {120568, 16856579}, {120569, 16856835}, {120570, 16857091}, - {120571, 17945347}, {120572, 16851715}, {120573, 16851971}, {120574, 16852227}, + {120571, 17945091}, {120572, 16851715}, {120573, 16851971}, {120574, 16852227}, {120575, 16852483}, {120576, 16852739}, {120577, 16852995}, {120578, 16853251}, {120579, 16853507}, {120580, 16846851}, {120581, 16853763}, {120582, 16854019}, {120583, 16786179}, {120584, 16854275}, {120585, 16854531}, {120586, 16854787}, {120587, 16855043}, {120588, 16855299}, {120589, 16855555}, {120591, 16855811}, {120592, 16856067}, {120593, 16856323}, {120594, 16856579}, {120595, 16856835}, - {120596, 16857091}, {120597, 17945603}, {120598, 16852739}, {120599, 16853507}, + {120596, 16857091}, {120597, 17945347}, {120598, 16852739}, {120599, 16853507}, {120600, 16853763}, {120601, 16856323}, {120602, 16855299}, {120603, 16855043}, {120604, 16851715}, {120605, 16851971}, {120606, 16852227}, {120607, 16852483}, {120608, 16852739}, {120609, 16852995}, {120610, 16853251}, {120611, 16853507}, @@ -2393,13 +2394,13 @@ const uint32_t table[8000][2] = {120616, 16854275}, {120617, 16854531}, {120618, 16854787}, {120619, 16855043}, {120620, 16855299}, {120621, 16853507}, {120622, 16855555}, {120623, 16855811}, {120624, 16856067}, {120625, 16856323}, {120626, 16856579}, {120627, 16856835}, - {120628, 16857091}, {120629, 17945347}, {120630, 16851715}, {120631, 16851971}, + {120628, 16857091}, {120629, 17945091}, {120630, 16851715}, {120631, 16851971}, {120632, 16852227}, {120633, 16852483}, {120634, 16852739}, {120635, 16852995}, {120636, 16853251}, {120637, 16853507}, {120638, 16846851}, {120639, 16853763}, {120640, 16854019}, {120641, 16786179}, {120642, 16854275}, {120643, 16854531}, {120644, 16854787}, {120645, 16855043}, {120646, 16855299}, {120647, 16855555}, {120649, 16855811}, {120650, 16856067}, {120651, 16856323}, {120652, 16856579}, - {120653, 16856835}, {120654, 16857091}, {120655, 17945603}, {120656, 16852739}, + {120653, 16856835}, {120654, 16857091}, {120655, 17945347}, {120656, 16852739}, {120657, 16853507}, {120658, 16853763}, {120659, 16856323}, {120660, 16855299}, {120661, 16855043}, {120662, 16851715}, {120663, 16851971}, {120664, 16852227}, {120665, 16852483}, {120666, 16852739}, {120667, 16852995}, {120668, 16853251}, @@ -2407,13 +2408,13 @@ const uint32_t table[8000][2] = {120673, 16786179}, {120674, 16854275}, {120675, 16854531}, {120676, 16854787}, {120677, 16855043}, {120678, 16855299}, {120679, 16853507}, {120680, 16855555}, {120681, 16855811}, {120682, 16856067}, {120683, 16856323}, {120684, 16856579}, - {120685, 16856835}, {120686, 16857091}, {120687, 17945347}, {120688, 16851715}, + {120685, 16856835}, {120686, 16857091}, {120687, 17945091}, {120688, 16851715}, {120689, 16851971}, {120690, 16852227}, {120691, 16852483}, {120692, 16852739}, {120693, 16852995}, {120694, 16853251}, {120695, 16853507}, {120696, 16846851}, {120697, 16853763}, {120698, 16854019}, {120699, 16786179}, {120700, 16854275}, {120701, 16854531}, {120702, 16854787}, {120703, 16855043}, {120704, 16855299}, {120705, 16855555}, {120707, 16855811}, {120708, 16856067}, {120709, 16856323}, - {120710, 16856579}, {120711, 16856835}, {120712, 16857091}, {120713, 17945603}, + {120710, 16856579}, {120711, 16856835}, {120712, 16857091}, {120713, 17945347}, {120714, 16852739}, {120715, 16853507}, {120716, 16853763}, {120717, 16856323}, {120718, 16855299}, {120719, 16855043}, {120720, 16851715}, {120721, 16851971}, {120722, 16852227}, {120723, 16852483}, {120724, 16852739}, {120725, 16852995}, @@ -2421,28 +2422,28 @@ const uint32_t table[8000][2] = {120730, 16854019}, {120731, 16786179}, {120732, 16854275}, {120733, 16854531}, {120734, 16854787}, {120735, 16855043}, {120736, 16855299}, {120737, 16853507}, {120738, 16855555}, {120739, 16855811}, {120740, 16856067}, {120741, 16856323}, - {120742, 16856579}, {120743, 16856835}, {120744, 16857091}, {120745, 17945347}, + {120742, 16856579}, {120743, 16856835}, {120744, 16857091}, {120745, 17945091}, {120746, 16851715}, {120747, 16851971}, {120748, 16852227}, {120749, 16852483}, {120750, 16852739}, {120751, 16852995}, {120752, 16853251}, {120753, 16853507}, {120754, 16846851}, {120755, 16853763}, {120756, 16854019}, {120757, 16786179}, {120758, 16854275}, {120759, 16854531}, {120760, 16854787}, {120761, 16855043}, {120762, 16855299}, {120763, 16855555}, {120765, 16855811}, {120766, 16856067}, {120767, 16856323}, {120768, 16856579}, {120769, 16856835}, {120770, 16857091}, - {120771, 17945603}, {120772, 16852739}, {120773, 16853507}, {120774, 16853763}, + {120771, 17945347}, {120772, 16852739}, {120773, 16853507}, {120774, 16853763}, {120775, 16856323}, {120776, 16855299}, {120777, 16855043}, {120778, 16858627}, - {120780, 2}, {120782, 17035523}, {120783, 16786947}, {120784, 16785155}, - {120785, 16785411}, {120786, 16787715}, {120787, 17035779}, {120788, 17036035}, - {120789, 17036291}, {120790, 17036547}, {120791, 17036803}, {120792, 17035523}, + {120780, 2}, {120782, 17035267}, {120783, 16786947}, {120784, 16785155}, + {120785, 16785411}, {120786, 16787715}, {120787, 17035523}, {120788, 17035779}, + {120789, 17036035}, {120790, 17036291}, {120791, 17036547}, {120792, 17035267}, {120793, 16786947}, {120794, 16785155}, {120795, 16785411}, {120796, 16787715}, - {120797, 17035779}, {120798, 17036035}, {120799, 17036291}, {120800, 17036547}, - {120801, 17036803}, {120802, 17035523}, {120803, 16786947}, {120804, 16785155}, - {120805, 16785411}, {120806, 16787715}, {120807, 17035779}, {120808, 17036035}, - {120809, 17036291}, {120810, 17036547}, {120811, 17036803}, {120812, 17035523}, + {120797, 17035523}, {120798, 17035779}, {120799, 17036035}, {120800, 17036291}, + {120801, 17036547}, {120802, 17035267}, {120803, 16786947}, {120804, 16785155}, + {120805, 16785411}, {120806, 16787715}, {120807, 17035523}, {120808, 17035779}, + {120809, 17036035}, {120810, 17036291}, {120811, 17036547}, {120812, 17035267}, {120813, 16786947}, {120814, 16785155}, {120815, 16785411}, {120816, 16787715}, - {120817, 17035779}, {120818, 17036035}, {120819, 17036291}, {120820, 17036547}, - {120821, 17036803}, {120822, 17035523}, {120823, 16786947}, {120824, 16785155}, - {120825, 16785411}, {120826, 16787715}, {120827, 17035779}, {120828, 17036035}, - {120829, 17036291}, {120830, 17036547}, {120831, 17036803}, {120832, 1}, + {120817, 17035523}, {120818, 17035779}, {120819, 17036035}, {120820, 17036291}, + {120821, 17036547}, {120822, 17035267}, {120823, 16786947}, {120824, 16785155}, + {120825, 16785411}, {120826, 16787715}, {120827, 17035523}, {120828, 17035779}, + {120829, 17036035}, {120830, 17036291}, {120831, 17036547}, {120832, 1}, {121484, 2}, {121499, 1}, {121504, 2}, {121505, 1}, {121520, 2}, {122624, 1}, {122655, 2}, {122661, 1}, {122667, 2}, {122880, 1}, {122887, 2}, {122888, 1}, @@ -2454,15 +2455,15 @@ const uint32_t table[8000][2] = {122941, 16870403}, {122942, 16870659}, {122943, 16870915}, {122944, 16871171}, {122945, 16871427}, {122946, 16871683}, {122947, 16871939}, {122948, 16872195}, {122949, 16872451}, {122950, 16872707}, {122951, 16873475}, {122952, 16873987}, - {122953, 16874243}, {122954, 17495299}, {122955, 16888835}, {122956, 16864003}, - {122957, 16864515}, {122958, 16890883}, {122959, 16883715}, {122960, 17945859}, + {122953, 16874243}, {122954, 17495043}, {122955, 16888835}, {122956, 16864003}, + {122957, 16864515}, {122958, 16890883}, {122959, 16883715}, {122960, 17945603}, {122961, 16866563}, {122962, 16866819}, {122963, 16867075}, {122964, 16867331}, {122965, 16867587}, {122966, 16867843}, {122967, 16868099}, {122968, 16868355}, {122969, 16868611}, {122970, 16869123}, {122971, 16869379}, {122972, 16870147}, {122973, 16870403}, {122974, 16870915}, {122975, 16871427}, {122976, 16871683}, {122977, 16871939}, {122978, 16872195}, {122979, 16872451}, {122980, 16872707}, {122981, 16873219}, {122982, 16873475}, {122983, 16879875}, {122984, 16864003}, - {122985, 16863747}, {122986, 16866307}, {122987, 16883203}, {122988, 17490435}, + {122985, 16863747}, {122986, 16866307}, {122987, 16883203}, {122988, 17490179}, {122989, 16883971}, {122990, 2}, {123023, 1}, {123024, 2}, {123136, 1}, {123181, 2}, {123184, 1}, {123198, 2}, {123200, 1}, {123210, 2}, {123214, 1}, {123216, 2}, @@ -2471,101 +2472,101 @@ const uint32_t table[8000][2] = {124896, 1}, {124903, 2}, {124904, 1}, {124908, 2}, {124909, 1}, {124911, 2}, {124912, 1}, {124927, 2}, {124928, 1}, {125125, 2}, {125127, 1}, {125143, 2}, - {125184, 17946115}, {125185, 17946371}, {125186, 17946627}, {125187, 17946883}, - {125188, 17947139}, {125189, 17947395}, {125190, 17947651}, {125191, 17947907}, - {125192, 17948163}, {125193, 17948419}, {125194, 17948675}, {125195, 17948931}, - {125196, 17949187}, {125197, 17949443}, {125198, 17949699}, {125199, 17949955}, - {125200, 17950211}, {125201, 17950467}, {125202, 17950723}, {125203, 17950979}, - {125204, 17951235}, {125205, 17951491}, {125206, 17951747}, {125207, 17952003}, - {125208, 17952259}, {125209, 17952515}, {125210, 17952771}, {125211, 17953027}, - {125212, 17953283}, {125213, 17953539}, {125214, 17953795}, {125215, 17954051}, - {125216, 17954307}, {125217, 17954563}, {125218, 1}, {125260, 2}, + {125184, 17945859}, {125185, 17946115}, {125186, 17946371}, {125187, 17946627}, + {125188, 17946883}, {125189, 17947139}, {125190, 17947395}, {125191, 17947651}, + {125192, 17947907}, {125193, 17948163}, {125194, 17948419}, {125195, 17948675}, + {125196, 17948931}, {125197, 17949187}, {125198, 17949443}, {125199, 17949699}, + {125200, 17949955}, {125201, 17950211}, {125202, 17950467}, {125203, 17950723}, + {125204, 17950979}, {125205, 17951235}, {125206, 17951491}, {125207, 17951747}, + {125208, 17952003}, {125209, 17952259}, {125210, 17952515}, {125211, 17952771}, + {125212, 17953027}, {125213, 17953283}, {125214, 17953539}, {125215, 17953795}, + {125216, 17954051}, {125217, 17954307}, {125218, 1}, {125260, 2}, {125264, 1}, {125274, 2}, {125278, 1}, {125280, 2}, {126065, 1}, {126133, 2}, {126209, 1}, {126270, 2}, - {126464, 16910339}, {126465, 17683715}, {126466, 17681923}, {126467, 17834499}, - {126468, 2}, {126469, 16910851}, {126470, 17731587}, {126471, 17682435}, - {126472, 17700099}, {126473, 16911875}, {126474, 17708803}, {126475, 17711107}, - {126476, 17682947}, {126477, 17718019}, {126478, 17694979}, {126479, 17701635}, - {126480, 17703683}, {126481, 17697027}, {126482, 17706755}, {126483, 17725187}, - {126484, 17745155}, {126485, 17686787}, {126486, 17689859}, {126487, 17684995}, - {126488, 17724675}, {126489, 17698051}, {126490, 17701123}, {126491, 17702659}, - {126492, 17954819}, {126493, 17673475}, {126494, 17955075}, {126495, 17955331}, - {126496, 2}, {126497, 17683715}, {126498, 17681923}, {126499, 2}, - {126500, 17721091}, {126501, 2}, {126503, 17682435}, {126504, 2}, - {126505, 16911875}, {126506, 17708803}, {126507, 17711107}, {126508, 17682947}, - {126509, 17718019}, {126510, 17694979}, {126511, 17701635}, {126512, 17703683}, - {126513, 17697027}, {126514, 17706755}, {126515, 2}, {126516, 17745155}, - {126517, 17686787}, {126518, 17689859}, {126519, 17684995}, {126520, 2}, - {126521, 17698051}, {126522, 2}, {126523, 17702659}, {126524, 2}, - {126530, 17681923}, {126531, 2}, {126535, 17682435}, {126536, 2}, - {126537, 16911875}, {126538, 2}, {126539, 17711107}, {126540, 2}, - {126541, 17718019}, {126542, 17694979}, {126543, 17701635}, {126544, 2}, - {126545, 17697027}, {126546, 17706755}, {126547, 2}, {126548, 17745155}, - {126549, 2}, {126551, 17684995}, {126552, 2}, {126553, 17698051}, - {126554, 2}, {126555, 17702659}, {126556, 2}, {126557, 17673475}, - {126558, 2}, {126559, 17955331}, {126560, 2}, {126561, 17683715}, - {126562, 17681923}, {126563, 2}, {126564, 17721091}, {126565, 2}, - {126567, 17682435}, {126568, 17700099}, {126569, 16911875}, {126570, 17708803}, - {126571, 2}, {126572, 17682947}, {126573, 17718019}, {126574, 17694979}, - {126575, 17701635}, {126576, 17703683}, {126577, 17697027}, {126578, 17706755}, - {126579, 2}, {126580, 17745155}, {126581, 17686787}, {126582, 17689859}, - {126583, 17684995}, {126584, 2}, {126585, 17698051}, {126586, 17701123}, - {126587, 17702659}, {126588, 17954819}, {126589, 2}, {126590, 17955075}, - {126591, 2}, {126592, 16910339}, {126593, 17683715}, {126594, 17681923}, - {126595, 17834499}, {126596, 17721091}, {126597, 16910851}, {126598, 17731587}, - {126599, 17682435}, {126600, 17700099}, {126601, 16911875}, {126602, 2}, - {126603, 17711107}, {126604, 17682947}, {126605, 17718019}, {126606, 17694979}, - {126607, 17701635}, {126608, 17703683}, {126609, 17697027}, {126610, 17706755}, - {126611, 17725187}, {126612, 17745155}, {126613, 17686787}, {126614, 17689859}, - {126615, 17684995}, {126616, 17724675}, {126617, 17698051}, {126618, 17701123}, - {126619, 17702659}, {126620, 2}, {126625, 17683715}, {126626, 17681923}, - {126627, 17834499}, {126628, 2}, {126629, 16910851}, {126630, 17731587}, - {126631, 17682435}, {126632, 17700099}, {126633, 16911875}, {126634, 2}, - {126635, 17711107}, {126636, 17682947}, {126637, 17718019}, {126638, 17694979}, - {126639, 17701635}, {126640, 17703683}, {126641, 17697027}, {126642, 17706755}, - {126643, 17725187}, {126644, 17745155}, {126645, 17686787}, {126646, 17689859}, - {126647, 17684995}, {126648, 17724675}, {126649, 17698051}, {126650, 17701123}, - {126651, 17702659}, {126652, 2}, {126704, 1}, {126706, 2}, + {126464, 16910339}, {126465, 17683459}, {126466, 17681667}, {126467, 17834243}, + {126468, 2}, {126469, 16910851}, {126470, 17731331}, {126471, 17682179}, + {126472, 17699843}, {126473, 16911875}, {126474, 17708547}, {126475, 17710851}, + {126476, 17682691}, {126477, 17717763}, {126478, 17694723}, {126479, 17701379}, + {126480, 17703427}, {126481, 17696771}, {126482, 17706499}, {126483, 17724931}, + {126484, 17744899}, {126485, 17686531}, {126486, 17689603}, {126487, 17684739}, + {126488, 17724419}, {126489, 17697795}, {126490, 17700867}, {126491, 17702403}, + {126492, 17954563}, {126493, 17673219}, {126494, 17954819}, {126495, 17955075}, + {126496, 2}, {126497, 17683459}, {126498, 17681667}, {126499, 2}, + {126500, 17720835}, {126501, 2}, {126503, 17682179}, {126504, 2}, + {126505, 16911875}, {126506, 17708547}, {126507, 17710851}, {126508, 17682691}, + {126509, 17717763}, {126510, 17694723}, {126511, 17701379}, {126512, 17703427}, + {126513, 17696771}, {126514, 17706499}, {126515, 2}, {126516, 17744899}, + {126517, 17686531}, {126518, 17689603}, {126519, 17684739}, {126520, 2}, + {126521, 17697795}, {126522, 2}, {126523, 17702403}, {126524, 2}, + {126530, 17681667}, {126531, 2}, {126535, 17682179}, {126536, 2}, + {126537, 16911875}, {126538, 2}, {126539, 17710851}, {126540, 2}, + {126541, 17717763}, {126542, 17694723}, {126543, 17701379}, {126544, 2}, + {126545, 17696771}, {126546, 17706499}, {126547, 2}, {126548, 17744899}, + {126549, 2}, {126551, 17684739}, {126552, 2}, {126553, 17697795}, + {126554, 2}, {126555, 17702403}, {126556, 2}, {126557, 17673219}, + {126558, 2}, {126559, 17955075}, {126560, 2}, {126561, 17683459}, + {126562, 17681667}, {126563, 2}, {126564, 17720835}, {126565, 2}, + {126567, 17682179}, {126568, 17699843}, {126569, 16911875}, {126570, 17708547}, + {126571, 2}, {126572, 17682691}, {126573, 17717763}, {126574, 17694723}, + {126575, 17701379}, {126576, 17703427}, {126577, 17696771}, {126578, 17706499}, + {126579, 2}, {126580, 17744899}, {126581, 17686531}, {126582, 17689603}, + {126583, 17684739}, {126584, 2}, {126585, 17697795}, {126586, 17700867}, + {126587, 17702403}, {126588, 17954563}, {126589, 2}, {126590, 17954819}, + {126591, 2}, {126592, 16910339}, {126593, 17683459}, {126594, 17681667}, + {126595, 17834243}, {126596, 17720835}, {126597, 16910851}, {126598, 17731331}, + {126599, 17682179}, {126600, 17699843}, {126601, 16911875}, {126602, 2}, + {126603, 17710851}, {126604, 17682691}, {126605, 17717763}, {126606, 17694723}, + {126607, 17701379}, {126608, 17703427}, {126609, 17696771}, {126610, 17706499}, + {126611, 17724931}, {126612, 17744899}, {126613, 17686531}, {126614, 17689603}, + {126615, 17684739}, {126616, 17724419}, {126617, 17697795}, {126618, 17700867}, + {126619, 17702403}, {126620, 2}, {126625, 17683459}, {126626, 17681667}, + {126627, 17834243}, {126628, 2}, {126629, 16910851}, {126630, 17731331}, + {126631, 17682179}, {126632, 17699843}, {126633, 16911875}, {126634, 2}, + {126635, 17710851}, {126636, 17682691}, {126637, 17717763}, {126638, 17694723}, + {126639, 17701379}, {126640, 17703427}, {126641, 17696771}, {126642, 17706499}, + {126643, 17724931}, {126644, 17744899}, {126645, 17686531}, {126646, 17689603}, + {126647, 17684739}, {126648, 17724419}, {126649, 17697795}, {126650, 17700867}, + {126651, 17702403}, {126652, 2}, {126704, 1}, {126706, 2}, {126976, 1}, {127020, 2}, {127024, 1}, {127124, 2}, {127136, 1}, {127151, 2}, {127153, 1}, {127168, 2}, {127169, 1}, {127184, 2}, {127185, 1}, {127222, 2}, - {127233, 34732803}, {127234, 34733315}, {127235, 34733827}, {127236, 34734339}, - {127237, 34734851}, {127238, 34735363}, {127239, 34735875}, {127240, 34736387}, - {127241, 34736899}, {127242, 34737411}, {127243, 1}, {127248, 50644995}, - {127249, 50645763}, {127250, 50646531}, {127251, 50647299}, {127252, 50648067}, - {127253, 50648835}, {127254, 50649603}, {127255, 50650371}, {127256, 50651139}, - {127257, 50651907}, {127258, 50652675}, {127259, 50653443}, {127260, 50654211}, - {127261, 50654979}, {127262, 50655747}, {127263, 50656515}, {127264, 50657283}, - {127265, 50658051}, {127266, 50658819}, {127267, 50659587}, {127268, 50660355}, - {127269, 50661123}, {127270, 50661891}, {127271, 50662659}, {127272, 50663427}, - {127273, 50664195}, {127274, 51515139}, {127275, 16777731}, {127276, 16781571}, - {127277, 33554947}, {127278, 34738691}, {127279, 1}, {127280, 16777219}, + {127233, 34732547}, {127234, 34733059}, {127235, 34733571}, {127236, 34734083}, + {127237, 34734595}, {127238, 34735107}, {127239, 34735619}, {127240, 34736131}, + {127241, 34736643}, {127242, 34737155}, {127243, 1}, {127248, 50644739}, + {127249, 50645507}, {127250, 50646275}, {127251, 50647043}, {127252, 50647811}, + {127253, 50648579}, {127254, 50649347}, {127255, 50650115}, {127256, 50650883}, + {127257, 50651651}, {127258, 50652419}, {127259, 50653187}, {127260, 50653955}, + {127261, 50654723}, {127262, 50655491}, {127263, 50656259}, {127264, 50657027}, + {127265, 50657795}, {127266, 50658563}, {127267, 50659331}, {127268, 50660099}, + {127269, 50660867}, {127270, 50661635}, {127271, 50662403}, {127272, 50663171}, + {127273, 50663939}, {127274, 51514883}, {127275, 16777731}, {127276, 16781571}, + {127277, 33554947}, {127278, 34738435}, {127279, 1}, {127280, 16777219}, {127281, 16777475}, {127282, 16777731}, {127283, 16777987}, {127284, 16778243}, {127285, 16778499}, {127286, 16778755}, {127287, 16779011}, {127288, 16779267}, {127289, 16779523}, {127290, 16779779}, {127291, 16780035}, {127292, 16780291}, {127293, 16780547}, {127294, 16780803}, {127295, 16781059}, {127296, 16781315}, {127297, 16781571}, {127298, 16781827}, {127299, 16782083}, {127300, 16782339}, {127301, 16782595}, {127302, 16782851}, {127303, 16783107}, {127304, 16783363}, - {127305, 16783619}, {127306, 34739203}, {127307, 34226691}, {127308, 34739715}, - {127309, 33752579}, {127310, 51517443}, {127311, 34740995}, {127312, 1}, - {127338, 34209539}, {127339, 34189571}, {127340, 34741507}, {127341, 1}, - {127376, 34742019}, {127377, 1}, {127406, 2}, {127462, 1}, - {127488, 34742531}, {127489, 34743043}, {127490, 17307907}, {127491, 2}, - {127504, 17157891}, {127505, 17966339}, {127506, 17966595}, {127507, 17351683}, - {127508, 17143299}, {127509, 17966851}, {127510, 17967107}, {127511, 17225475}, - {127512, 17967363}, {127513, 17967619}, {127514, 17967875}, {127515, 17584643}, - {127516, 17968131}, {127517, 17968387}, {127518, 17968643}, {127519, 17968899}, - {127520, 17969155}, {127521, 17969411}, {127522, 17167107}, {127523, 17969667}, - {127524, 17969923}, {127525, 17970179}, {127526, 17970435}, {127527, 17970691}, - {127528, 17970947}, {127529, 17141763}, {127530, 17223427}, {127531, 17971203}, - {127532, 17288707}, {127533, 17224195}, {127534, 17288963}, {127535, 17971459}, - {127536, 17181443}, {127537, 17971715}, {127538, 17971971}, {127539, 17972227}, - {127540, 17972483}, {127541, 17972739}, {127542, 17264387}, {127543, 17160451}, - {127544, 17972995}, {127545, 17973251}, {127546, 17973507}, {127547, 17973763}, - {127548, 2}, {127552, 51528451}, {127553, 51529219}, {127554, 51529987}, - {127555, 51530755}, {127556, 51531523}, {127557, 51532291}, {127558, 51533059}, - {127559, 51533827}, {127560, 51534595}, {127561, 2}, {127568, 17980931}, - {127569, 17981187}, {127570, 2}, {127584, 1}, {127590, 2}, + {127305, 16783619}, {127306, 34738947}, {127307, 34226435}, {127308, 34739459}, + {127309, 34739971}, {127310, 51517699}, {127311, 34741251}, {127312, 1}, + {127338, 34209283}, {127339, 34189315}, {127340, 34741763}, {127341, 1}, + {127376, 34742275}, {127377, 1}, {127406, 2}, {127462, 1}, + {127488, 34742787}, {127489, 34743299}, {127490, 17307651}, {127491, 2}, + {127504, 17157635}, {127505, 17966595}, {127506, 17966851}, {127507, 17351427}, + {127508, 17143043}, {127509, 17967107}, {127510, 17967363}, {127511, 17225219}, + {127512, 17967619}, {127513, 17967875}, {127514, 17968131}, {127515, 17584387}, + {127516, 17968387}, {127517, 17968643}, {127518, 17968899}, {127519, 17969155}, + {127520, 17969411}, {127521, 17969667}, {127522, 17166851}, {127523, 17969923}, + {127524, 17970179}, {127525, 17970435}, {127526, 17970691}, {127527, 17970947}, + {127528, 17971203}, {127529, 17141507}, {127530, 17223171}, {127531, 17971459}, + {127532, 17288451}, {127533, 17223939}, {127534, 17288707}, {127535, 17971715}, + {127536, 17181187}, {127537, 17971971}, {127538, 17972227}, {127539, 17972483}, + {127540, 17972739}, {127541, 17972995}, {127542, 17264131}, {127543, 17160195}, + {127544, 17973251}, {127545, 17973507}, {127546, 17973763}, {127547, 17974019}, + {127548, 2}, {127552, 51528707}, {127553, 51529475}, {127554, 51530243}, + {127555, 51531011}, {127556, 51531779}, {127557, 51532547}, {127558, 51533315}, + {127559, 51534083}, {127560, 51534851}, {127561, 2}, {127568, 17981187}, + {127569, 17981443}, {127570, 2}, {127584, 1}, {127590, 2}, {127744, 1}, {128728, 2}, {128732, 1}, {128749, 2}, {128752, 1}, {128765, 2}, {128768, 1}, {128887, 2}, {128891, 1}, {128986, 2}, {128992, 1}, {129004, 2}, @@ -2578,152 +2579,152 @@ const uint32_t table[8000][2] = {129727, 1}, {129734, 2}, {129742, 1}, {129756, 2}, {129760, 1}, {129769, 2}, {129776, 1}, {129785, 2}, {129792, 1}, {129939, 2}, {129940, 1}, {129995, 2}, - {130032, 17035523}, {130033, 16786947}, {130034, 16785155}, {130035, 16785411}, - {130036, 16787715}, {130037, 17035779}, {130038, 17036035}, {130039, 17036291}, - {130040, 17036547}, {130041, 17036803}, {130042, 2}, {131072, 1}, + {130032, 17035267}, {130033, 16786947}, {130034, 16785155}, {130035, 16785411}, + {130036, 16787715}, {130037, 17035523}, {130038, 17035779}, {130039, 17036035}, + {130040, 17036291}, {130041, 17036547}, {130042, 2}, {131072, 1}, {173792, 2}, {173824, 1}, {177978, 2}, {177984, 1}, {178206, 2}, {178208, 1}, {183970, 2}, {183984, 1}, - {191457, 2}, {194560, 17981443}, {194561, 17981699}, {194562, 17981955}, - {194563, 17982211}, {194564, 17982467}, {194565, 17608451}, {194566, 17982723}, - {194567, 17982979}, {194568, 17983235}, {194569, 17983491}, {194570, 17608707}, - {194571, 17983747}, {194572, 17984003}, {194573, 17984259}, {194574, 17608963}, - {194575, 17984515}, {194576, 17984771}, {194577, 17985027}, {194578, 17985283}, - {194579, 17985539}, {194580, 17985795}, {194581, 17968643}, {194582, 17986051}, - {194583, 17986307}, {194584, 17986563}, {194585, 17986819}, {194586, 17987075}, - {194587, 17623043}, {194588, 17987331}, {194589, 17145859}, {194590, 17987587}, - {194591, 17987843}, {194592, 17988099}, {194593, 17988355}, {194594, 17973251}, - {194595, 17988611}, {194596, 17988867}, {194597, 17624323}, {194598, 17609219}, - {194599, 17609475}, {194600, 17624579}, {194601, 17989123}, {194602, 17989379}, - {194603, 17562883}, {194604, 17989635}, {194605, 17609731}, {194606, 17989891}, - {194607, 17990147}, {194608, 17990403}, {194609, 17990659}, {194612, 17990915}, - {194613, 17991171}, {194614, 17991427}, {194615, 17991683}, {194616, 17991939}, - {194617, 17992195}, {194618, 17992451}, {194619, 17992707}, {194620, 17992963}, - {194621, 17993219}, {194622, 17993475}, {194623, 17993731}, {194624, 17993987}, - {194625, 17994243}, {194626, 17994499}, {194627, 17994755}, {194628, 17995011}, - {194629, 17995267}, {194631, 17625091}, {194632, 17995523}, {194633, 17995779}, - {194634, 17996035}, {194635, 17996291}, {194636, 17610243}, {194637, 17996547}, - {194638, 17996803}, {194639, 17997059}, {194640, 17600003}, {194641, 17997315}, - {194642, 17997571}, {194643, 17997827}, {194644, 17998083}, {194645, 17998339}, - {194646, 17998595}, {194647, 17998851}, {194648, 17999107}, {194649, 17999363}, - {194650, 17999619}, {194651, 17999875}, {194652, 18000131}, {194653, 17966851}, - {194654, 18000387}, {194655, 18000643}, {194656, 18000899}, {194657, 18001155}, - {194658, 18001411}, {194659, 18001667}, {194660, 18001923}, {194661, 18002179}, - {194662, 18002435}, {194663, 18002691}, {194664, 2}, {194665, 18002947}, - {194666, 18003203}, {194668, 18003459}, {194669, 18003715}, {194670, 18003971}, - {194671, 17561859}, {194672, 18004227}, {194673, 18004483}, {194674, 18004739}, - {194675, 18004995}, {194676, 2}, {194677, 17152515}, {194678, 18005251}, - {194679, 18005507}, {194680, 17153027}, {194681, 18005763}, {194682, 18006019}, - {194683, 18006275}, {194684, 18006531}, {194685, 18006787}, {194686, 18007043}, - {194687, 18007299}, {194688, 18007555}, {194689, 18007811}, {194690, 18008067}, - {194691, 18008323}, {194692, 18008579}, {194693, 18008835}, {194694, 18009091}, - {194695, 18009347}, {194696, 18009603}, {194697, 18009859}, {194698, 18010115}, - {194699, 18010371}, {194700, 18010627}, {194701, 18010883}, {194702, 17548547}, - {194703, 18011139}, {194704, 17155587}, {194705, 18011395}, {194707, 18011651}, - {194708, 18011907}, {194710, 18012163}, {194711, 18012419}, {194712, 18012675}, - {194713, 18012931}, {194714, 18013187}, {194715, 18013443}, {194716, 18013699}, - {194717, 18013955}, {194718, 18014211}, {194719, 18014467}, {194720, 18014723}, - {194721, 18014979}, {194722, 18015235}, {194723, 17611523}, {194724, 18015491}, - {194725, 18015747}, {194726, 18016003}, {194727, 18016259}, {194728, 17628163}, - {194729, 18016259}, {194730, 18016515}, {194731, 17612035}, {194732, 18016771}, - {194733, 18017027}, {194734, 18017283}, {194735, 18017539}, {194736, 17612291}, - {194737, 17541635}, {194738, 17414915}, {194739, 18017795}, {194740, 18018051}, - {194741, 18018307}, {194742, 18018563}, {194743, 18018819}, {194744, 18019075}, - {194745, 18019331}, {194746, 18019587}, {194747, 18019843}, {194748, 18020099}, - {194749, 18020355}, {194750, 18020611}, {194751, 18020867}, {194752, 18021123}, - {194753, 18021379}, {194754, 18021635}, {194755, 18021891}, {194756, 18022147}, - {194757, 18022403}, {194758, 18022659}, {194759, 18022915}, {194760, 17612547}, - {194761, 18023171}, {194762, 18023427}, {194763, 18023683}, {194764, 18023939}, - {194765, 18024195}, {194766, 18024451}, {194767, 17613059}, {194768, 18024707}, - {194769, 18024963}, {194770, 18025219}, {194771, 18025475}, {194772, 18025731}, - {194773, 18025987}, {194774, 18026243}, {194775, 18026499}, {194776, 17548803}, - {194777, 17630211}, {194778, 18026755}, {194779, 18027011}, {194780, 18027267}, - {194781, 18027523}, {194782, 18027779}, {194783, 18028035}, {194784, 18028291}, - {194785, 18028547}, {194786, 17613315}, {194787, 18028803}, {194788, 18029059}, - {194789, 18029315}, {194790, 18029571}, {194791, 17640963}, {194792, 18029827}, - {194793, 18030083}, {194794, 18030339}, {194795, 18030595}, {194796, 18030851}, - {194797, 18031107}, {194798, 18031363}, {194799, 18031619}, {194800, 18031875}, - {194801, 18032131}, {194802, 18032387}, {194803, 18032643}, {194804, 18032899}, - {194805, 17566211}, {194806, 18033155}, {194807, 18033411}, {194808, 18033667}, - {194809, 18033923}, {194810, 18034179}, {194811, 18034435}, {194812, 18034691}, - {194813, 18034947}, {194814, 18035203}, {194815, 18035459}, {194816, 18035715}, - {194817, 17613571}, {194818, 17587203}, {194819, 18035971}, {194820, 18036227}, - {194821, 18036483}, {194822, 18036739}, {194823, 18036995}, {194824, 18037251}, - {194825, 18037507}, {194826, 18037763}, {194827, 17630979}, {194828, 18038019}, - {194829, 18038275}, {194830, 18038531}, {194831, 18038787}, {194832, 18039043}, - {194833, 18039299}, {194834, 18039555}, {194835, 18039811}, {194836, 17631235}, - {194837, 18040067}, {194838, 18040323}, {194839, 18040579}, {194840, 18040835}, - {194841, 18041091}, {194842, 18041347}, {194843, 18041603}, {194844, 18041859}, - {194845, 18042115}, {194846, 18042371}, {194847, 2}, {194848, 18042627}, - {194849, 17631747}, {194850, 18042883}, {194851, 18043139}, {194852, 18043395}, - {194853, 18043651}, {194854, 18043907}, {194855, 18044163}, {194856, 18044419}, - {194857, 18044675}, {194858, 18044931}, {194859, 18045187}, {194860, 18045443}, - {194862, 18045699}, {194863, 18045955}, {194864, 17632259}, {194865, 18046211}, - {194866, 18046467}, {194867, 18046723}, {194868, 18046979}, {194869, 18047235}, - {194870, 18047491}, {194871, 18047747}, {194872, 17562627}, {194873, 18048003}, - {194874, 18048259}, {194875, 18048515}, {194876, 18048771}, {194877, 18049027}, - {194878, 18049283}, {194879, 18049539}, {194880, 17633795}, {194881, 18049795}, - {194882, 18050051}, {194883, 18050307}, {194884, 18050563}, {194885, 18050819}, - {194886, 18051075}, {194888, 17634051}, {194889, 17641475}, {194890, 18051331}, - {194891, 18051587}, {194892, 18051843}, {194893, 18052099}, {194894, 18052355}, - {194895, 17553155}, {194896, 17634563}, {194897, 18052611}, {194898, 18052867}, - {194899, 17616131}, {194900, 18053123}, {194901, 18053379}, {194902, 17605123}, - {194903, 18053635}, {194904, 18053891}, {194905, 17616899}, {194906, 18054147}, - {194907, 18054403}, {194908, 18054659}, {194909, 18054915}, {194911, 2}, - {194912, 18055171}, {194913, 18055427}, {194914, 18055683}, {194915, 18055939}, - {194916, 18056195}, {194917, 18056451}, {194918, 18056707}, {194919, 18056963}, - {194920, 18057219}, {194921, 18057475}, {194922, 18057731}, {194923, 18057987}, - {194924, 18058243}, {194925, 18058499}, {194926, 18058755}, {194927, 18059011}, - {194928, 18059267}, {194929, 18059523}, {194930, 18059779}, {194931, 18060035}, - {194932, 18060291}, {194933, 18060547}, {194934, 18060803}, {194935, 18061059}, - {194936, 18061315}, {194937, 18061571}, {194938, 17618435}, {194939, 18061827}, - {194940, 18062083}, {194941, 18062339}, {194942, 18062595}, {194943, 18062851}, - {194944, 18063107}, {194945, 18063363}, {194946, 18063619}, {194947, 18063875}, - {194948, 18064131}, {194949, 18064387}, {194950, 18064643}, {194951, 18064899}, - {194952, 18065155}, {194953, 18065411}, {194954, 18065667}, {194955, 18011651}, - {194956, 18065923}, {194957, 18066179}, {194958, 18066435}, {194959, 18066691}, - {194960, 18066947}, {194961, 18067203}, {194962, 18067459}, {194963, 18067715}, - {194964, 18067971}, {194965, 18068227}, {194966, 18068483}, {194967, 18068739}, - {194968, 17566979}, {194969, 18068995}, {194970, 18069251}, {194971, 18069507}, - {194972, 18069763}, {194973, 18070019}, {194974, 18070275}, {194975, 17619203}, - {194976, 18070531}, {194977, 18070787}, {194978, 18071043}, {194979, 18071299}, - {194980, 18071555}, {194981, 18071811}, {194982, 18072067}, {194983, 18072323}, - {194984, 18072579}, {194985, 18072835}, {194986, 18073091}, {194987, 18073347}, - {194988, 18073603}, {194989, 18073859}, {194990, 18074115}, {194991, 18074371}, - {194992, 18074627}, {194993, 18074883}, {194994, 18075139}, {194995, 18075395}, - {194996, 17551875}, {194997, 18075651}, {194998, 18075907}, {194999, 18076163}, - {195000, 18076419}, {195001, 18076675}, {195002, 18076931}, {195003, 17636355}, - {195004, 18077187}, {195005, 18077443}, {195006, 18077699}, {195007, 2}, - {195008, 18077955}, {195009, 18078211}, {195010, 18078467}, {195011, 18078723}, - {195012, 17178627}, {195013, 18078979}, {195014, 18079235}, {195015, 18079491}, - {195016, 18079747}, {195017, 18080003}, {195018, 18080259}, {195019, 18080515}, - {195020, 18080771}, {195021, 18081027}, {195022, 18081283}, {195023, 18081539}, - {195024, 17637635}, {195025, 17637891}, {195026, 17180419}, {195027, 18081795}, - {195028, 18082051}, {195029, 18082307}, {195030, 18082563}, {195031, 18082819}, - {195032, 18083075}, {195033, 18083331}, {195034, 18083587}, {195035, 18083843}, - {195036, 18084099}, {195037, 18084355}, {195038, 18084611}, {195039, 17638147}, - {195040, 18084867}, {195041, 18085123}, {195042, 18085379}, {195043, 18085635}, - {195044, 18085891}, {195045, 18086147}, {195046, 18086403}, {195047, 18086659}, - {195048, 18086915}, {195049, 18087171}, {195050, 18087427}, {195051, 18087683}, - {195052, 18087939}, {195053, 18088195}, {195054, 18088451}, {195055, 18088707}, - {195056, 18088963}, {195057, 18089219}, {195058, 18089475}, {195059, 18089731}, - {195060, 18089987}, {195061, 18090243}, {195062, 18090499}, {195063, 18090755}, - {195064, 18091011}, {195065, 18091267}, {195066, 18091523}, {195067, 18091779}, - {195068, 18092035}, {195069, 18092291}, {195070, 17639683}, {195072, 18092547}, - {195073, 18092803}, {195074, 18093059}, {195075, 18093315}, {195076, 18093571}, - {195077, 18093827}, {195078, 18094083}, {195079, 18094339}, {195080, 18094595}, - {195081, 18094851}, {195082, 17639939}, {195083, 18095107}, {195084, 18095363}, - {195085, 18095619}, {195086, 18095875}, {195087, 18096131}, {195088, 18096387}, - {195089, 18096643}, {195090, 18096899}, {195091, 18097155}, {195092, 18097411}, - {195093, 17192707}, {195094, 18097667}, {195095, 17193731}, {195096, 18097923}, - {195097, 18098179}, {195098, 18098435}, {195099, 18098691}, {195100, 17195011}, - {195101, 18098947}, {195102, 2}, {196608, 1}, {201547, 2}, - {201552, 1}, {205744, 2}, {917760, 0}, {918000, 2} + {191457, 2}, {191472, 1}, {192094, 2}, {194560, 17981699}, + {194561, 17981955}, {194562, 17982211}, {194563, 17982467}, {194564, 17982723}, + {194565, 17608195}, {194566, 17982979}, {194567, 17983235}, {194568, 17983491}, + {194569, 17983747}, {194570, 17608451}, {194571, 17984003}, {194572, 17984259}, + {194573, 17984515}, {194574, 17608707}, {194575, 17984771}, {194576, 17985027}, + {194577, 17985283}, {194578, 17985539}, {194579, 17985795}, {194580, 17986051}, + {194581, 17968899}, {194582, 17986307}, {194583, 17986563}, {194584, 17986819}, + {194585, 17987075}, {194586, 17987331}, {194587, 17622787}, {194588, 17987587}, + {194589, 17145603}, {194590, 17987843}, {194591, 17988099}, {194592, 17988355}, + {194593, 17988611}, {194594, 17973507}, {194595, 17988867}, {194596, 17989123}, + {194597, 17624067}, {194598, 17608963}, {194599, 17609219}, {194600, 17624323}, + {194601, 17989379}, {194602, 17989635}, {194603, 17562627}, {194604, 17989891}, + {194605, 17609475}, {194606, 17990147}, {194607, 17990403}, {194608, 17990659}, + {194609, 17990915}, {194612, 17991171}, {194613, 17991427}, {194614, 17991683}, + {194615, 17991939}, {194616, 17992195}, {194617, 17992451}, {194618, 17992707}, + {194619, 17992963}, {194620, 17993219}, {194621, 17993475}, {194622, 17993731}, + {194623, 17993987}, {194624, 17994243}, {194625, 17994499}, {194626, 17994755}, + {194627, 17995011}, {194628, 17995267}, {194629, 17995523}, {194631, 17624835}, + {194632, 17995779}, {194633, 17996035}, {194634, 17996291}, {194635, 17996547}, + {194636, 17609987}, {194637, 17996803}, {194638, 17997059}, {194639, 17997315}, + {194640, 17599747}, {194641, 17997571}, {194642, 17997827}, {194643, 17998083}, + {194644, 17998339}, {194645, 17998595}, {194646, 17998851}, {194647, 17999107}, + {194648, 17999363}, {194649, 17999619}, {194650, 17999875}, {194651, 18000131}, + {194652, 18000387}, {194653, 17967107}, {194654, 18000643}, {194655, 18000899}, + {194656, 18001155}, {194657, 18001411}, {194658, 18001667}, {194659, 18001923}, + {194660, 18002179}, {194661, 18002435}, {194662, 18002691}, {194663, 18002947}, + {194664, 2}, {194665, 18003203}, {194666, 18003459}, {194668, 18003715}, + {194669, 18003971}, {194670, 18004227}, {194671, 17561603}, {194672, 18004483}, + {194673, 18004739}, {194674, 18004995}, {194675, 18005251}, {194676, 2}, + {194677, 17152259}, {194678, 18005507}, {194679, 18005763}, {194680, 17152771}, + {194681, 18006019}, {194682, 18006275}, {194683, 18006531}, {194684, 18006787}, + {194685, 18007043}, {194686, 18007299}, {194687, 18007555}, {194688, 18007811}, + {194689, 18008067}, {194690, 18008323}, {194691, 18008579}, {194692, 18008835}, + {194693, 18009091}, {194694, 18009347}, {194695, 18009603}, {194696, 18009859}, + {194697, 18010115}, {194698, 18010371}, {194699, 18010627}, {194700, 18010883}, + {194701, 18011139}, {194702, 17548291}, {194703, 18011395}, {194704, 17155331}, + {194705, 18011651}, {194707, 18011907}, {194708, 18012163}, {194710, 18012419}, + {194711, 18012675}, {194712, 18012931}, {194713, 18013187}, {194714, 18013443}, + {194715, 18013699}, {194716, 18013955}, {194717, 18014211}, {194718, 18014467}, + {194719, 18014723}, {194720, 18014979}, {194721, 18015235}, {194722, 18015491}, + {194723, 17611267}, {194724, 18015747}, {194725, 18016003}, {194726, 18016259}, + {194727, 18016515}, {194728, 17627907}, {194729, 18016515}, {194730, 18016771}, + {194731, 17611779}, {194732, 18017027}, {194733, 18017283}, {194734, 18017539}, + {194735, 18017795}, {194736, 17612035}, {194737, 17541379}, {194738, 17414659}, + {194739, 18018051}, {194740, 18018307}, {194741, 18018563}, {194742, 18018819}, + {194743, 18019075}, {194744, 18019331}, {194745, 18019587}, {194746, 18019843}, + {194747, 18020099}, {194748, 18020355}, {194749, 18020611}, {194750, 18020867}, + {194751, 18021123}, {194752, 18021379}, {194753, 18021635}, {194754, 18021891}, + {194755, 18022147}, {194756, 18022403}, {194757, 18022659}, {194758, 18022915}, + {194759, 18023171}, {194760, 17612291}, {194761, 18023427}, {194762, 18023683}, + {194763, 18023939}, {194764, 18024195}, {194765, 18024451}, {194766, 18024707}, + {194767, 17612803}, {194768, 18024963}, {194769, 18025219}, {194770, 18025475}, + {194771, 18025731}, {194772, 18025987}, {194773, 18026243}, {194774, 18026499}, + {194775, 18026755}, {194776, 17548547}, {194777, 17629955}, {194778, 18027011}, + {194779, 18027267}, {194780, 18027523}, {194781, 18027779}, {194782, 18028035}, + {194783, 18028291}, {194784, 18028547}, {194785, 18028803}, {194786, 17613059}, + {194787, 18029059}, {194788, 18029315}, {194789, 18029571}, {194790, 18029827}, + {194791, 17640707}, {194792, 18030083}, {194793, 18030339}, {194794, 18030595}, + {194795, 18030851}, {194796, 18031107}, {194797, 18031363}, {194798, 18031619}, + {194799, 18031875}, {194800, 18032131}, {194801, 18032387}, {194802, 18032643}, + {194803, 18032899}, {194804, 18033155}, {194805, 17565955}, {194806, 18033411}, + {194807, 18033667}, {194808, 18033923}, {194809, 18034179}, {194810, 18034435}, + {194811, 18034691}, {194812, 18034947}, {194813, 18035203}, {194814, 18035459}, + {194815, 18035715}, {194816, 18035971}, {194817, 17613315}, {194818, 17586947}, + {194819, 18036227}, {194820, 18036483}, {194821, 18036739}, {194822, 18036995}, + {194823, 18037251}, {194824, 18037507}, {194825, 18037763}, {194826, 18038019}, + {194827, 17630723}, {194828, 18038275}, {194829, 18038531}, {194830, 18038787}, + {194831, 18039043}, {194832, 18039299}, {194833, 18039555}, {194834, 18039811}, + {194835, 18040067}, {194836, 17630979}, {194837, 18040323}, {194838, 18040579}, + {194839, 18040835}, {194840, 18041091}, {194841, 18041347}, {194842, 18041603}, + {194843, 18041859}, {194844, 18042115}, {194845, 18042371}, {194846, 18042627}, + {194847, 2}, {194848, 18042883}, {194849, 17631491}, {194850, 18043139}, + {194851, 18043395}, {194852, 18043651}, {194853, 18043907}, {194854, 18044163}, + {194855, 18044419}, {194856, 18044675}, {194857, 18044931}, {194858, 18045187}, + {194859, 18045443}, {194860, 18045699}, {194862, 18045955}, {194863, 18046211}, + {194864, 17632003}, {194865, 18046467}, {194866, 18046723}, {194867, 18046979}, + {194868, 18047235}, {194869, 18047491}, {194870, 18047747}, {194871, 18048003}, + {194872, 17562371}, {194873, 18048259}, {194874, 18048515}, {194875, 18048771}, + {194876, 18049027}, {194877, 18049283}, {194878, 18049539}, {194879, 18049795}, + {194880, 17633539}, {194881, 18050051}, {194882, 18050307}, {194883, 18050563}, + {194884, 18050819}, {194885, 18051075}, {194886, 18051331}, {194888, 17633795}, + {194889, 17641219}, {194890, 18051587}, {194891, 18051843}, {194892, 18052099}, + {194893, 18052355}, {194894, 18052611}, {194895, 17552899}, {194896, 17634307}, + {194897, 18052867}, {194898, 18053123}, {194899, 17615875}, {194900, 18053379}, + {194901, 18053635}, {194902, 17604867}, {194903, 18053891}, {194904, 18054147}, + {194905, 17616643}, {194906, 18054403}, {194907, 18054659}, {194908, 18054915}, + {194909, 18055171}, {194911, 2}, {194912, 18055427}, {194913, 18055683}, + {194914, 18055939}, {194915, 18056195}, {194916, 18056451}, {194917, 18056707}, + {194918, 18056963}, {194919, 18057219}, {194920, 18057475}, {194921, 18057731}, + {194922, 18057987}, {194923, 18058243}, {194924, 18058499}, {194925, 18058755}, + {194926, 18059011}, {194927, 18059267}, {194928, 18059523}, {194929, 18059779}, + {194930, 18060035}, {194931, 18060291}, {194932, 18060547}, {194933, 18060803}, + {194934, 18061059}, {194935, 18061315}, {194936, 18061571}, {194937, 18061827}, + {194938, 17618179}, {194939, 18062083}, {194940, 18062339}, {194941, 18062595}, + {194942, 18062851}, {194943, 18063107}, {194944, 18063363}, {194945, 18063619}, + {194946, 18063875}, {194947, 18064131}, {194948, 18064387}, {194949, 18064643}, + {194950, 18064899}, {194951, 18065155}, {194952, 18065411}, {194953, 18065667}, + {194954, 18065923}, {194955, 18011907}, {194956, 18066179}, {194957, 18066435}, + {194958, 18066691}, {194959, 18066947}, {194960, 18067203}, {194961, 18067459}, + {194962, 18067715}, {194963, 18067971}, {194964, 18068227}, {194965, 18068483}, + {194966, 18068739}, {194967, 18068995}, {194968, 17566723}, {194969, 18069251}, + {194970, 18069507}, {194971, 18069763}, {194972, 18070019}, {194973, 18070275}, + {194974, 18070531}, {194975, 17618947}, {194976, 18070787}, {194977, 18071043}, + {194978, 18071299}, {194979, 18071555}, {194980, 18071811}, {194981, 18072067}, + {194982, 18072323}, {194983, 18072579}, {194984, 18072835}, {194985, 18073091}, + {194986, 18073347}, {194987, 18073603}, {194988, 18073859}, {194989, 18074115}, + {194990, 18074371}, {194991, 18074627}, {194992, 18074883}, {194993, 18075139}, + {194994, 18075395}, {194995, 18075651}, {194996, 17551619}, {194997, 18075907}, + {194998, 18076163}, {194999, 18076419}, {195000, 18076675}, {195001, 18076931}, + {195002, 18077187}, {195003, 17636099}, {195004, 18077443}, {195005, 18077699}, + {195006, 18077955}, {195007, 2}, {195008, 18078211}, {195009, 18078467}, + {195010, 18078723}, {195011, 18078979}, {195012, 17178371}, {195013, 18079235}, + {195014, 18079491}, {195015, 18079747}, {195016, 18080003}, {195017, 18080259}, + {195018, 18080515}, {195019, 18080771}, {195020, 18081027}, {195021, 18081283}, + {195022, 18081539}, {195023, 18081795}, {195024, 17637379}, {195025, 17637635}, + {195026, 17180163}, {195027, 18082051}, {195028, 18082307}, {195029, 18082563}, + {195030, 18082819}, {195031, 18083075}, {195032, 18083331}, {195033, 18083587}, + {195034, 18083843}, {195035, 18084099}, {195036, 18084355}, {195037, 18084611}, + {195038, 18084867}, {195039, 17637891}, {195040, 18085123}, {195041, 18085379}, + {195042, 18085635}, {195043, 18085891}, {195044, 18086147}, {195045, 18086403}, + {195046, 18086659}, {195047, 18086915}, {195048, 18087171}, {195049, 18087427}, + {195050, 18087683}, {195051, 18087939}, {195052, 18088195}, {195053, 18088451}, + {195054, 18088707}, {195055, 18088963}, {195056, 18089219}, {195057, 18089475}, + {195058, 18089731}, {195059, 18089987}, {195060, 18090243}, {195061, 18090499}, + {195062, 18090755}, {195063, 18091011}, {195064, 18091267}, {195065, 18091523}, + {195066, 18091779}, {195067, 18092035}, {195068, 18092291}, {195069, 18092547}, + {195070, 17639427}, {195072, 18092803}, {195073, 18093059}, {195074, 18093315}, + {195075, 18093571}, {195076, 18093827}, {195077, 18094083}, {195078, 18094339}, + {195079, 18094595}, {195080, 18094851}, {195081, 18095107}, {195082, 17639683}, + {195083, 18095363}, {195084, 18095619}, {195085, 18095875}, {195086, 18096131}, + {195087, 18096387}, {195088, 18096643}, {195089, 18096899}, {195090, 18097155}, + {195091, 18097411}, {195092, 18097667}, {195093, 17192451}, {195094, 18097923}, + {195095, 17193475}, {195096, 18098179}, {195097, 18098435}, {195098, 18098691}, + {195099, 18098947}, {195100, 17194755}, {195101, 18099203}, {195102, 2}, + {196608, 1}, {201547, 2}, {201552, 1}, {205744, 2}, + {917760, 0}, {918000, 2} }; } // namespace ada::idna #endif // ADA_IDNA_TABLES_H - /* end file src/mapping_tables.cpp */ namespace ada::idna { @@ -9522,22 +9523,6 @@ bool is_label_valid(const std::u32string_view label) { namespace ada::idna { -bool begins_with(std::u32string_view view, std::u32string_view prefix) { - if (view.size() < prefix.size()) { - return false; - } - // constexpr as of C++20 - return std::equal(prefix.begin(), prefix.end(), view.begin()); -} - -bool begins_with(std::string_view view, std::string_view prefix) { - if (view.size() < prefix.size()) { - return false; - } - // constexpr as of C++20 - return std::equal(prefix.begin(), prefix.end(), view.begin()); -} - bool constexpr is_ascii(std::u32string_view view) { for (uint32_t c : view) { if (c >= 0x80) { @@ -9600,7 +9585,7 @@ static std::string from_ascii_to_ascii(std::string_view ut8_string) { label_start += label_size_with_dot; if (label_size == 0) { // empty label? Nothing to do. - } else if (begins_with(label_view, "xn--")) { + } else if (label_view.starts_with("xn--")) { // The xn-- part is the expensive game. out.append(label_view); std::string_view puny_segment_ascii( @@ -9667,7 +9652,7 @@ std::string to_ascii(std::string_view ut8_string) { label_start += label_size_with_dot; if (label_size == 0) { // empty label? Nothing to do. - } else if (begins_with(label_view, U"xn--")) { + } else if (label_view.starts_with(U"xn--")) { // we do not need to check, e.g., Xn-- because mapping goes to lower case for (char32_t c : label_view) { if (c >= 0x80) { @@ -9746,8 +9731,7 @@ std::string to_unicode(std::string_view input) { is_last_label ? input.size() - label_start : loc_dot - label_start; auto label_view = std::string_view(input.data() + label_start, label_size); - if (ada::idna::begins_with(label_view, "xn--") && - ada::idna::is_ascii(label_view)) { + if (label_view.starts_with("xn--") && ada::idna::is_ascii(label_view)) { label_view.remove_prefix(4); if (ada::idna::verify_punycode(label_view)) { std::u32string tmp_buffer; @@ -9782,6 +9766,644 @@ std::string to_unicode(std::string_view input) { } } // namespace ada::idna /* end file src/to_unicode.cpp */ +/* begin file src/identifier.cpp */ + +#include +#include +#include + +/* begin file src/id_tables.cpp */ +// IDNA 15.1.0 + +// clang-format off +#ifndef ADA_IDNA_IDENTIFIER_TABLES_H +#define ADA_IDNA_IDENTIFIER_TABLES_H +#include + +namespace ada::idna { + +const uint32_t id_continue[1344][2] = +{ + {48, 57}, {65, 90}, {95, 95}, {97, 122}, + {170, 170}, {181, 181}, {183, 183}, {186, 186}, + {192, 214}, {216, 246}, {248, 442}, {443, 443}, + {444, 447}, {448, 451}, {452, 659}, {660, 660}, + {661, 687}, {688, 705}, {710, 721}, {736, 740}, + {748, 748}, {750, 750}, {768, 879}, {880, 883}, + {884, 884}, {886, 887}, {890, 890}, {891, 893}, + {895, 895}, {902, 902}, {903, 903}, {904, 906}, + {908, 908}, {910, 929}, {931, 1013}, {1015, 1153}, + {1155, 1159}, {1162, 1327}, {1329, 1366}, {1369, 1369}, + {1376, 1416}, {1425, 1469}, {1471, 1471}, {1473, 1474}, + {1476, 1477}, {1479, 1479}, {1488, 1514}, {1519, 1522}, + {1552, 1562}, {1568, 1599}, {1600, 1600}, {1601, 1610}, + {1611, 1631}, {1632, 1641}, {1646, 1647}, {1648, 1648}, + {1649, 1747}, {1749, 1749}, {1750, 1756}, {1759, 1764}, + {1765, 1766}, {1767, 1768}, {1770, 1773}, {1774, 1775}, + {1776, 1785}, {1786, 1788}, {1791, 1791}, {1808, 1808}, + {1809, 1809}, {1810, 1839}, {1840, 1866}, {1869, 1957}, + {1958, 1968}, {1969, 1969}, {1984, 1993}, {1994, 2026}, + {2027, 2035}, {2036, 2037}, {2042, 2042}, {2045, 2045}, + {2048, 2069}, {2070, 2073}, {2074, 2074}, {2075, 2083}, + {2084, 2084}, {2085, 2087}, {2088, 2088}, {2089, 2093}, + {2112, 2136}, {2137, 2139}, {2144, 2154}, {2160, 2183}, + {2185, 2190}, {2200, 2207}, {2208, 2248}, {2249, 2249}, + {2250, 2273}, {2275, 2306}, {2307, 2307}, {2308, 2361}, + {2362, 2362}, {2363, 2363}, {2364, 2364}, {2365, 2365}, + {2366, 2368}, {2369, 2376}, {2377, 2380}, {2381, 2381}, + {2382, 2383}, {2384, 2384}, {2385, 2391}, {2392, 2401}, + {2402, 2403}, {2406, 2415}, {2417, 2417}, {2418, 2432}, + {2433, 2433}, {2434, 2435}, {2437, 2444}, {2447, 2448}, + {2451, 2472}, {2474, 2480}, {2482, 2482}, {2486, 2489}, + {2492, 2492}, {2493, 2493}, {2494, 2496}, {2497, 2500}, + {2503, 2504}, {2507, 2508}, {2509, 2509}, {2510, 2510}, + {2519, 2519}, {2524, 2525}, {2527, 2529}, {2530, 2531}, + {2534, 2543}, {2544, 2545}, {2556, 2556}, {2558, 2558}, + {2561, 2562}, {2563, 2563}, {2565, 2570}, {2575, 2576}, + {2579, 2600}, {2602, 2608}, {2610, 2611}, {2613, 2614}, + {2616, 2617}, {2620, 2620}, {2622, 2624}, {2625, 2626}, + {2631, 2632}, {2635, 2637}, {2641, 2641}, {2649, 2652}, + {2654, 2654}, {2662, 2671}, {2672, 2673}, {2674, 2676}, + {2677, 2677}, {2689, 2690}, {2691, 2691}, {2693, 2701}, + {2703, 2705}, {2707, 2728}, {2730, 2736}, {2738, 2739}, + {2741, 2745}, {2748, 2748}, {2749, 2749}, {2750, 2752}, + {2753, 2757}, {2759, 2760}, {2761, 2761}, {2763, 2764}, + {2765, 2765}, {2768, 2768}, {2784, 2785}, {2786, 2787}, + {2790, 2799}, {2809, 2809}, {2810, 2815}, {2817, 2817}, + {2818, 2819}, {2821, 2828}, {2831, 2832}, {2835, 2856}, + {2858, 2864}, {2866, 2867}, {2869, 2873}, {2876, 2876}, + {2877, 2877}, {2878, 2878}, {2879, 2879}, {2880, 2880}, + {2881, 2884}, {2887, 2888}, {2891, 2892}, {2893, 2893}, + {2901, 2902}, {2903, 2903}, {2908, 2909}, {2911, 2913}, + {2914, 2915}, {2918, 2927}, {2929, 2929}, {2946, 2946}, + {2947, 2947}, {2949, 2954}, {2958, 2960}, {2962, 2965}, + {2969, 2970}, {2972, 2972}, {2974, 2975}, {2979, 2980}, + {2984, 2986}, {2990, 3001}, {3006, 3007}, {3008, 3008}, + {3009, 3010}, {3014, 3016}, {3018, 3020}, {3021, 3021}, + {3024, 3024}, {3031, 3031}, {3046, 3055}, {3072, 3072}, + {3073, 3075}, {3076, 3076}, {3077, 3084}, {3086, 3088}, + {3090, 3112}, {3114, 3129}, {3132, 3132}, {3133, 3133}, + {3134, 3136}, {3137, 3140}, {3142, 3144}, {3146, 3149}, + {3157, 3158}, {3160, 3162}, {3165, 3165}, {3168, 3169}, + {3170, 3171}, {3174, 3183}, {3200, 3200}, {3201, 3201}, + {3202, 3203}, {3205, 3212}, {3214, 3216}, {3218, 3240}, + {3242, 3251}, {3253, 3257}, {3260, 3260}, {3261, 3261}, + {3262, 3262}, {3263, 3263}, {3264, 3268}, {3270, 3270}, + {3271, 3272}, {3274, 3275}, {3276, 3277}, {3285, 3286}, + {3293, 3294}, {3296, 3297}, {3298, 3299}, {3302, 3311}, + {3313, 3314}, {3315, 3315}, {3328, 3329}, {3330, 3331}, + {3332, 3340}, {3342, 3344}, {3346, 3386}, {3387, 3388}, + {3389, 3389}, {3390, 3392}, {3393, 3396}, {3398, 3400}, + {3402, 3404}, {3405, 3405}, {3406, 3406}, {3412, 3414}, + {3415, 3415}, {3423, 3425}, {3426, 3427}, {3430, 3439}, + {3450, 3455}, {3457, 3457}, {3458, 3459}, {3461, 3478}, + {3482, 3505}, {3507, 3515}, {3517, 3517}, {3520, 3526}, + {3530, 3530}, {3535, 3537}, {3538, 3540}, {3542, 3542}, + {3544, 3551}, {3558, 3567}, {3570, 3571}, {3585, 3632}, + {3633, 3633}, {3634, 3635}, {3636, 3642}, {3648, 3653}, + {3654, 3654}, {3655, 3662}, {3664, 3673}, {3713, 3714}, + {3716, 3716}, {3718, 3722}, {3724, 3747}, {3749, 3749}, + {3751, 3760}, {3761, 3761}, {3762, 3763}, {3764, 3772}, + {3773, 3773}, {3776, 3780}, {3782, 3782}, {3784, 3790}, + {3792, 3801}, {3804, 3807}, {3840, 3840}, {3864, 3865}, + {3872, 3881}, {3893, 3893}, {3895, 3895}, {3897, 3897}, + {3902, 3903}, {3904, 3911}, {3913, 3948}, {3953, 3966}, + {3967, 3967}, {3968, 3972}, {3974, 3975}, {3976, 3980}, + {3981, 3991}, {3993, 4028}, {4038, 4038}, {4096, 4138}, + {4139, 4140}, {4141, 4144}, {4145, 4145}, {4146, 4151}, + {4152, 4152}, {4153, 4154}, {4155, 4156}, {4157, 4158}, + {4159, 4159}, {4160, 4169}, {4176, 4181}, {4182, 4183}, + {4184, 4185}, {4186, 4189}, {4190, 4192}, {4193, 4193}, + {4194, 4196}, {4197, 4198}, {4199, 4205}, {4206, 4208}, + {4209, 4212}, {4213, 4225}, {4226, 4226}, {4227, 4228}, + {4229, 4230}, {4231, 4236}, {4237, 4237}, {4238, 4238}, + {4239, 4239}, {4240, 4249}, {4250, 4252}, {4253, 4253}, + {4256, 4293}, {4295, 4295}, {4301, 4301}, {4304, 4346}, + {4348, 4348}, {4349, 4351}, {4352, 4680}, {4682, 4685}, + {4688, 4694}, {4696, 4696}, {4698, 4701}, {4704, 4744}, + {4746, 4749}, {4752, 4784}, {4786, 4789}, {4792, 4798}, + {4800, 4800}, {4802, 4805}, {4808, 4822}, {4824, 4880}, + {4882, 4885}, {4888, 4954}, {4957, 4959}, {4969, 4977}, + {4992, 5007}, {5024, 5109}, {5112, 5117}, {5121, 5740}, + {5743, 5759}, {5761, 5786}, {5792, 5866}, {5870, 5872}, + {5873, 5880}, {5888, 5905}, {5906, 5908}, {5909, 5909}, + {5919, 5937}, {5938, 5939}, {5940, 5940}, {5952, 5969}, + {5970, 5971}, {5984, 5996}, {5998, 6000}, {6002, 6003}, + {6016, 6067}, {6068, 6069}, {6070, 6070}, {6071, 6077}, + {6078, 6085}, {6086, 6086}, {6087, 6088}, {6089, 6099}, + {6103, 6103}, {6108, 6108}, {6109, 6109}, {6112, 6121}, + {6155, 6157}, {6159, 6159}, {6160, 6169}, {6176, 6210}, + {6211, 6211}, {6212, 6264}, {6272, 6276}, {6277, 6278}, + {6279, 6312}, {6313, 6313}, {6314, 6314}, {6320, 6389}, + {6400, 6430}, {6432, 6434}, {6435, 6438}, {6439, 6440}, + {6441, 6443}, {6448, 6449}, {6450, 6450}, {6451, 6456}, + {6457, 6459}, {6470, 6479}, {6480, 6509}, {6512, 6516}, + {6528, 6571}, {6576, 6601}, {6608, 6617}, {6618, 6618}, + {6656, 6678}, {6679, 6680}, {6681, 6682}, {6683, 6683}, + {6688, 6740}, {6741, 6741}, {6742, 6742}, {6743, 6743}, + {6744, 6750}, {6752, 6752}, {6753, 6753}, {6754, 6754}, + {6755, 6756}, {6757, 6764}, {6765, 6770}, {6771, 6780}, + {6783, 6783}, {6784, 6793}, {6800, 6809}, {6823, 6823}, + {6832, 6845}, {6847, 6862}, {6912, 6915}, {6916, 6916}, + {6917, 6963}, {6964, 6964}, {6965, 6965}, {6966, 6970}, + {6971, 6971}, {6972, 6972}, {6973, 6977}, {6978, 6978}, + {6979, 6980}, {6981, 6988}, {6992, 7001}, {7019, 7027}, + {7040, 7041}, {7042, 7042}, {7043, 7072}, {7073, 7073}, + {7074, 7077}, {7078, 7079}, {7080, 7081}, {7082, 7082}, + {7083, 7085}, {7086, 7087}, {7088, 7097}, {7098, 7141}, + {7142, 7142}, {7143, 7143}, {7144, 7145}, {7146, 7148}, + {7149, 7149}, {7150, 7150}, {7151, 7153}, {7154, 7155}, + {7168, 7203}, {7204, 7211}, {7212, 7219}, {7220, 7221}, + {7222, 7223}, {7232, 7241}, {7245, 7247}, {7248, 7257}, + {7258, 7287}, {7288, 7293}, {7296, 7304}, {7312, 7354}, + {7357, 7359}, {7376, 7378}, {7380, 7392}, {7393, 7393}, + {7394, 7400}, {7401, 7404}, {7405, 7405}, {7406, 7411}, + {7412, 7412}, {7413, 7414}, {7415, 7415}, {7416, 7417}, + {7418, 7418}, {7424, 7467}, {7468, 7530}, {7531, 7543}, + {7544, 7544}, {7545, 7578}, {7579, 7615}, {7616, 7679}, + {7680, 7957}, {7960, 7965}, {7968, 8005}, {8008, 8013}, + {8016, 8023}, {8025, 8025}, {8027, 8027}, {8029, 8029}, + {8031, 8061}, {8064, 8116}, {8118, 8124}, {8126, 8126}, + {8130, 8132}, {8134, 8140}, {8144, 8147}, {8150, 8155}, + {8160, 8172}, {8178, 8180}, {8182, 8188}, {8204, 8205}, + {8255, 8256}, {8276, 8276}, {8305, 8305}, {8319, 8319}, + {8336, 8348}, {8400, 8412}, {8417, 8417}, {8421, 8432}, + {8450, 8450}, {8455, 8455}, {8458, 8467}, {8469, 8469}, + {8472, 8472}, {8473, 8477}, {8484, 8484}, {8486, 8486}, + {8488, 8488}, {8490, 8493}, {8494, 8494}, {8495, 8500}, + {8501, 8504}, {8505, 8505}, {8508, 8511}, {8517, 8521}, + {8526, 8526}, {8544, 8578}, {8579, 8580}, {8581, 8584}, + {11264, 11387}, {11388, 11389}, {11390, 11492}, {11499, 11502}, + {11503, 11505}, {11506, 11507}, {11520, 11557}, {11559, 11559}, + {11565, 11565}, {11568, 11623}, {11631, 11631}, {11647, 11647}, + {11648, 11670}, {11680, 11686}, {11688, 11694}, {11696, 11702}, + {11704, 11710}, {11712, 11718}, {11720, 11726}, {11728, 11734}, + {11736, 11742}, {11744, 11775}, {12293, 12293}, {12294, 12294}, + {12295, 12295}, {12321, 12329}, {12330, 12333}, {12334, 12335}, + {12337, 12341}, {12344, 12346}, {12347, 12347}, {12348, 12348}, + {12353, 12438}, {12441, 12442}, {12443, 12444}, {12445, 12446}, + {12447, 12447}, {12449, 12538}, {12539, 12539}, {12540, 12542}, + {12543, 12543}, {12549, 12591}, {12593, 12686}, {12704, 12735}, + {12784, 12799}, {13312, 19903}, {19968, 40980}, {40981, 40981}, + {40982, 42124}, {42192, 42231}, {42232, 42237}, {42240, 42507}, + {42508, 42508}, {42512, 42527}, {42528, 42537}, {42538, 42539}, + {42560, 42605}, {42606, 42606}, {42607, 42607}, {42612, 42621}, + {42623, 42623}, {42624, 42651}, {42652, 42653}, {42654, 42655}, + {42656, 42725}, {42726, 42735}, {42736, 42737}, {42775, 42783}, + {42786, 42863}, {42864, 42864}, {42865, 42887}, {42888, 42888}, + {42891, 42894}, {42895, 42895}, {42896, 42954}, {42960, 42961}, + {42963, 42963}, {42965, 42969}, {42994, 42996}, {42997, 42998}, + {42999, 42999}, {43000, 43001}, {43002, 43002}, {43003, 43009}, + {43010, 43010}, {43011, 43013}, {43014, 43014}, {43015, 43018}, + {43019, 43019}, {43020, 43042}, {43043, 43044}, {43045, 43046}, + {43047, 43047}, {43052, 43052}, {43072, 43123}, {43136, 43137}, + {43138, 43187}, {43188, 43203}, {43204, 43205}, {43216, 43225}, + {43232, 43249}, {43250, 43255}, {43259, 43259}, {43261, 43262}, + {43263, 43263}, {43264, 43273}, {43274, 43301}, {43302, 43309}, + {43312, 43334}, {43335, 43345}, {43346, 43347}, {43360, 43388}, + {43392, 43394}, {43395, 43395}, {43396, 43442}, {43443, 43443}, + {43444, 43445}, {43446, 43449}, {43450, 43451}, {43452, 43453}, + {43454, 43456}, {43471, 43471}, {43472, 43481}, {43488, 43492}, + {43493, 43493}, {43494, 43494}, {43495, 43503}, {43504, 43513}, + {43514, 43518}, {43520, 43560}, {43561, 43566}, {43567, 43568}, + {43569, 43570}, {43571, 43572}, {43573, 43574}, {43584, 43586}, + {43587, 43587}, {43588, 43595}, {43596, 43596}, {43597, 43597}, + {43600, 43609}, {43616, 43631}, {43632, 43632}, {43633, 43638}, + {43642, 43642}, {43643, 43643}, {43644, 43644}, {43645, 43645}, + {43646, 43695}, {43696, 43696}, {43697, 43697}, {43698, 43700}, + {43701, 43702}, {43703, 43704}, {43705, 43709}, {43710, 43711}, + {43712, 43712}, {43713, 43713}, {43714, 43714}, {43739, 43740}, + {43741, 43741}, {43744, 43754}, {43755, 43755}, {43756, 43757}, + {43758, 43759}, {43762, 43762}, {43763, 43764}, {43765, 43765}, + {43766, 43766}, {43777, 43782}, {43785, 43790}, {43793, 43798}, + {43808, 43814}, {43816, 43822}, {43824, 43866}, {43868, 43871}, + {43872, 43880}, {43881, 43881}, {43888, 43967}, {43968, 44002}, + {44003, 44004}, {44005, 44005}, {44006, 44007}, {44008, 44008}, + {44009, 44010}, {44012, 44012}, {44013, 44013}, {44016, 44025}, + {44032, 55203}, {55216, 55238}, {55243, 55291}, {63744, 64109}, + {64112, 64217}, {64256, 64262}, {64275, 64279}, {64285, 64285}, + {64286, 64286}, {64287, 64296}, {64298, 64310}, {64312, 64316}, + {64318, 64318}, {64320, 64321}, {64323, 64324}, {64326, 64433}, + {64467, 64829}, {64848, 64911}, {64914, 64967}, {65008, 65019}, + {65024, 65039}, {65056, 65071}, {65075, 65076}, {65101, 65103}, + {65136, 65140}, {65142, 65276}, {65296, 65305}, {65313, 65338}, + {65343, 65343}, {65345, 65370}, {65381, 65381}, {65382, 65391}, + {65392, 65392}, {65393, 65437}, {65438, 65439}, {65440, 65470}, + {65474, 65479}, {65482, 65487}, {65490, 65495}, {65498, 65500}, + {65536, 65547}, {65549, 65574}, {65576, 65594}, {65596, 65597}, + {65599, 65613}, {65616, 65629}, {65664, 65786}, {65856, 65908}, + {66045, 66045}, {66176, 66204}, {66208, 66256}, {66272, 66272}, + {66304, 66335}, {66349, 66368}, {66369, 66369}, {66370, 66377}, + {66378, 66378}, {66384, 66421}, {66422, 66426}, {66432, 66461}, + {66464, 66499}, {66504, 66511}, {66513, 66517}, {66560, 66639}, + {66640, 66717}, {66720, 66729}, {66736, 66771}, {66776, 66811}, + {66816, 66855}, {66864, 66915}, {66928, 66938}, {66940, 66954}, + {66956, 66962}, {66964, 66965}, {66967, 66977}, {66979, 66993}, + {66995, 67001}, {67003, 67004}, {67072, 67382}, {67392, 67413}, + {67424, 67431}, {67456, 67461}, {67463, 67504}, {67506, 67514}, + {67584, 67589}, {67592, 67592}, {67594, 67637}, {67639, 67640}, + {67644, 67644}, {67647, 67669}, {67680, 67702}, {67712, 67742}, + {67808, 67826}, {67828, 67829}, {67840, 67861}, {67872, 67897}, + {67968, 68023}, {68030, 68031}, {68096, 68096}, {68097, 68099}, + {68101, 68102}, {68108, 68111}, {68112, 68115}, {68117, 68119}, + {68121, 68149}, {68152, 68154}, {68159, 68159}, {68192, 68220}, + {68224, 68252}, {68288, 68295}, {68297, 68324}, {68325, 68326}, + {68352, 68405}, {68416, 68437}, {68448, 68466}, {68480, 68497}, + {68608, 68680}, {68736, 68786}, {68800, 68850}, {68864, 68899}, + {68900, 68903}, {68912, 68921}, {69248, 69289}, {69291, 69292}, + {69296, 69297}, {69373, 69375}, {69376, 69404}, {69415, 69415}, + {69424, 69445}, {69446, 69456}, {69488, 69505}, {69506, 69509}, + {69552, 69572}, {69600, 69622}, {69632, 69632}, {69633, 69633}, + {69634, 69634}, {69635, 69687}, {69688, 69702}, {69734, 69743}, + {69744, 69744}, {69745, 69746}, {69747, 69748}, {69749, 69749}, + {69759, 69761}, {69762, 69762}, {69763, 69807}, {69808, 69810}, + {69811, 69814}, {69815, 69816}, {69817, 69818}, {69826, 69826}, + {69840, 69864}, {69872, 69881}, {69888, 69890}, {69891, 69926}, + {69927, 69931}, {69932, 69932}, {69933, 69940}, {69942, 69951}, + {69956, 69956}, {69957, 69958}, {69959, 69959}, {69968, 70002}, + {70003, 70003}, {70006, 70006}, {70016, 70017}, {70018, 70018}, + {70019, 70066}, {70067, 70069}, {70070, 70078}, {70079, 70080}, + {70081, 70084}, {70089, 70092}, {70094, 70094}, {70095, 70095}, + {70096, 70105}, {70106, 70106}, {70108, 70108}, {70144, 70161}, + {70163, 70187}, {70188, 70190}, {70191, 70193}, {70194, 70195}, + {70196, 70196}, {70197, 70197}, {70198, 70199}, {70206, 70206}, + {70207, 70208}, {70209, 70209}, {70272, 70278}, {70280, 70280}, + {70282, 70285}, {70287, 70301}, {70303, 70312}, {70320, 70366}, + {70367, 70367}, {70368, 70370}, {70371, 70378}, {70384, 70393}, + {70400, 70401}, {70402, 70403}, {70405, 70412}, {70415, 70416}, + {70419, 70440}, {70442, 70448}, {70450, 70451}, {70453, 70457}, + {70459, 70460}, {70461, 70461}, {70462, 70463}, {70464, 70464}, + {70465, 70468}, {70471, 70472}, {70475, 70477}, {70480, 70480}, + {70487, 70487}, {70493, 70497}, {70498, 70499}, {70502, 70508}, + {70512, 70516}, {70656, 70708}, {70709, 70711}, {70712, 70719}, + {70720, 70721}, {70722, 70724}, {70725, 70725}, {70726, 70726}, + {70727, 70730}, {70736, 70745}, {70750, 70750}, {70751, 70753}, + {70784, 70831}, {70832, 70834}, {70835, 70840}, {70841, 70841}, + {70842, 70842}, {70843, 70846}, {70847, 70848}, {70849, 70849}, + {70850, 70851}, {70852, 70853}, {70855, 70855}, {70864, 70873}, + {71040, 71086}, {71087, 71089}, {71090, 71093}, {71096, 71099}, + {71100, 71101}, {71102, 71102}, {71103, 71104}, {71128, 71131}, + {71132, 71133}, {71168, 71215}, {71216, 71218}, {71219, 71226}, + {71227, 71228}, {71229, 71229}, {71230, 71230}, {71231, 71232}, + {71236, 71236}, {71248, 71257}, {71296, 71338}, {71339, 71339}, + {71340, 71340}, {71341, 71341}, {71342, 71343}, {71344, 71349}, + {71350, 71350}, {71351, 71351}, {71352, 71352}, {71360, 71369}, + {71424, 71450}, {71453, 71455}, {71456, 71457}, {71458, 71461}, + {71462, 71462}, {71463, 71467}, {71472, 71481}, {71488, 71494}, + {71680, 71723}, {71724, 71726}, {71727, 71735}, {71736, 71736}, + {71737, 71738}, {71840, 71903}, {71904, 71913}, {71935, 71942}, + {71945, 71945}, {71948, 71955}, {71957, 71958}, {71960, 71983}, + {71984, 71989}, {71991, 71992}, {71995, 71996}, {71997, 71997}, + {71998, 71998}, {71999, 71999}, {72000, 72000}, {72001, 72001}, + {72002, 72002}, {72003, 72003}, {72016, 72025}, {72096, 72103}, + {72106, 72144}, {72145, 72147}, {72148, 72151}, {72154, 72155}, + {72156, 72159}, {72160, 72160}, {72161, 72161}, {72163, 72163}, + {72164, 72164}, {72192, 72192}, {72193, 72202}, {72203, 72242}, + {72243, 72248}, {72249, 72249}, {72250, 72250}, {72251, 72254}, + {72263, 72263}, {72272, 72272}, {72273, 72278}, {72279, 72280}, + {72281, 72283}, {72284, 72329}, {72330, 72342}, {72343, 72343}, + {72344, 72345}, {72349, 72349}, {72368, 72440}, {72704, 72712}, + {72714, 72750}, {72751, 72751}, {72752, 72758}, {72760, 72765}, + {72766, 72766}, {72767, 72767}, {72768, 72768}, {72784, 72793}, + {72818, 72847}, {72850, 72871}, {72873, 72873}, {72874, 72880}, + {72881, 72881}, {72882, 72883}, {72884, 72884}, {72885, 72886}, + {72960, 72966}, {72968, 72969}, {72971, 73008}, {73009, 73014}, + {73018, 73018}, {73020, 73021}, {73023, 73029}, {73030, 73030}, + {73031, 73031}, {73040, 73049}, {73056, 73061}, {73063, 73064}, + {73066, 73097}, {73098, 73102}, {73104, 73105}, {73107, 73108}, + {73109, 73109}, {73110, 73110}, {73111, 73111}, {73112, 73112}, + {73120, 73129}, {73440, 73458}, {73459, 73460}, {73461, 73462}, + {73472, 73473}, {73474, 73474}, {73475, 73475}, {73476, 73488}, + {73490, 73523}, {73524, 73525}, {73526, 73530}, {73534, 73535}, + {73536, 73536}, {73537, 73537}, {73538, 73538}, {73552, 73561}, + {73648, 73648}, {73728, 74649}, {74752, 74862}, {74880, 75075}, + {77712, 77808}, {77824, 78895}, {78912, 78912}, {78913, 78918}, + {78919, 78933}, {82944, 83526}, {92160, 92728}, {92736, 92766}, + {92768, 92777}, {92784, 92862}, {92864, 92873}, {92880, 92909}, + {92912, 92916}, {92928, 92975}, {92976, 92982}, {92992, 92995}, + {93008, 93017}, {93027, 93047}, {93053, 93071}, {93760, 93823}, + {93952, 94026}, {94031, 94031}, {94032, 94032}, {94033, 94087}, + {94095, 94098}, {94099, 94111}, {94176, 94177}, {94179, 94179}, + {94180, 94180}, {94192, 94193}, {94208, 100343}, {100352, 101589}, + {101632, 101640}, {110576, 110579}, {110581, 110587}, {110589, 110590}, + {110592, 110882}, {110898, 110898}, {110928, 110930}, {110933, 110933}, + {110948, 110951}, {110960, 111355}, {113664, 113770}, {113776, 113788}, + {113792, 113800}, {113808, 113817}, {113821, 113822}, {118528, 118573}, + {118576, 118598}, {119141, 119142}, {119143, 119145}, {119149, 119154}, + {119163, 119170}, {119173, 119179}, {119210, 119213}, {119362, 119364}, + {119808, 119892}, {119894, 119964}, {119966, 119967}, {119970, 119970}, + {119973, 119974}, {119977, 119980}, {119982, 119993}, {119995, 119995}, + {119997, 120003}, {120005, 120069}, {120071, 120074}, {120077, 120084}, + {120086, 120092}, {120094, 120121}, {120123, 120126}, {120128, 120132}, + {120134, 120134}, {120138, 120144}, {120146, 120485}, {120488, 120512}, + {120514, 120538}, {120540, 120570}, {120572, 120596}, {120598, 120628}, + {120630, 120654}, {120656, 120686}, {120688, 120712}, {120714, 120744}, + {120746, 120770}, {120772, 120779}, {120782, 120831}, {121344, 121398}, + {121403, 121452}, {121461, 121461}, {121476, 121476}, {121499, 121503}, + {121505, 121519}, {122624, 122633}, {122634, 122634}, {122635, 122654}, + {122661, 122666}, {122880, 122886}, {122888, 122904}, {122907, 122913}, + {122915, 122916}, {122918, 122922}, {122928, 122989}, {123023, 123023}, + {123136, 123180}, {123184, 123190}, {123191, 123197}, {123200, 123209}, + {123214, 123214}, {123536, 123565}, {123566, 123566}, {123584, 123627}, + {123628, 123631}, {123632, 123641}, {124112, 124138}, {124139, 124139}, + {124140, 124143}, {124144, 124153}, {124896, 124902}, {124904, 124907}, + {124909, 124910}, {124912, 124926}, {124928, 125124}, {125136, 125142}, + {125184, 125251}, {125252, 125258}, {125259, 125259}, {125264, 125273}, + {126464, 126467}, {126469, 126495}, {126497, 126498}, {126500, 126500}, + {126503, 126503}, {126505, 126514}, {126516, 126519}, {126521, 126521}, + {126523, 126523}, {126530, 126530}, {126535, 126535}, {126537, 126537}, + {126539, 126539}, {126541, 126543}, {126545, 126546}, {126548, 126548}, + {126551, 126551}, {126553, 126553}, {126555, 126555}, {126557, 126557}, + {126559, 126559}, {126561, 126562}, {126564, 126564}, {126567, 126570}, + {126572, 126578}, {126580, 126583}, {126585, 126588}, {126590, 126590}, + {126592, 126601}, {126603, 126619}, {126625, 126627}, {126629, 126633}, + {126635, 126651}, {130032, 130041}, {131072, 173791}, {173824, 177977}, + {177984, 178205}, {178208, 183969}, {183984, 191456}, {191472, 192093}, + {194560, 195101}, {196608, 201546}, {201552, 205743}, {917760, 917999} +}; +const uint32_t id_start[740][2] = +{ + {65, 90}, {97, 122}, {170, 170}, {181, 181}, + {186, 186}, {192, 214}, {216, 246}, {248, 442}, + {443, 443}, {444, 447}, {448, 451}, {452, 659}, + {660, 660}, {661, 687}, {688, 705}, {710, 721}, + {736, 740}, {748, 748}, {750, 750}, {880, 883}, + {884, 884}, {886, 887}, {890, 890}, {891, 893}, + {895, 895}, {902, 902}, {904, 906}, {908, 908}, + {910, 929}, {931, 1013}, {1015, 1153}, {1162, 1327}, + {1329, 1366}, {1369, 1369}, {1376, 1416}, {1488, 1514}, + {1519, 1522}, {1568, 1599}, {1600, 1600}, {1601, 1610}, + {1646, 1647}, {1649, 1747}, {1749, 1749}, {1765, 1766}, + {1774, 1775}, {1786, 1788}, {1791, 1791}, {1808, 1808}, + {1810, 1839}, {1869, 1957}, {1969, 1969}, {1994, 2026}, + {2036, 2037}, {2042, 2042}, {2048, 2069}, {2074, 2074}, + {2084, 2084}, {2088, 2088}, {2112, 2136}, {2144, 2154}, + {2160, 2183}, {2185, 2190}, {2208, 2248}, {2249, 2249}, + {2308, 2361}, {2365, 2365}, {2384, 2384}, {2392, 2401}, + {2417, 2417}, {2418, 2432}, {2437, 2444}, {2447, 2448}, + {2451, 2472}, {2474, 2480}, {2482, 2482}, {2486, 2489}, + {2493, 2493}, {2510, 2510}, {2524, 2525}, {2527, 2529}, + {2544, 2545}, {2556, 2556}, {2565, 2570}, {2575, 2576}, + {2579, 2600}, {2602, 2608}, {2610, 2611}, {2613, 2614}, + {2616, 2617}, {2649, 2652}, {2654, 2654}, {2674, 2676}, + {2693, 2701}, {2703, 2705}, {2707, 2728}, {2730, 2736}, + {2738, 2739}, {2741, 2745}, {2749, 2749}, {2768, 2768}, + {2784, 2785}, {2809, 2809}, {2821, 2828}, {2831, 2832}, + {2835, 2856}, {2858, 2864}, {2866, 2867}, {2869, 2873}, + {2877, 2877}, {2908, 2909}, {2911, 2913}, {2929, 2929}, + {2947, 2947}, {2949, 2954}, {2958, 2960}, {2962, 2965}, + {2969, 2970}, {2972, 2972}, {2974, 2975}, {2979, 2980}, + {2984, 2986}, {2990, 3001}, {3024, 3024}, {3077, 3084}, + {3086, 3088}, {3090, 3112}, {3114, 3129}, {3133, 3133}, + {3160, 3162}, {3165, 3165}, {3168, 3169}, {3200, 3200}, + {3205, 3212}, {3214, 3216}, {3218, 3240}, {3242, 3251}, + {3253, 3257}, {3261, 3261}, {3293, 3294}, {3296, 3297}, + {3313, 3314}, {3332, 3340}, {3342, 3344}, {3346, 3386}, + {3389, 3389}, {3406, 3406}, {3412, 3414}, {3423, 3425}, + {3450, 3455}, {3461, 3478}, {3482, 3505}, {3507, 3515}, + {3517, 3517}, {3520, 3526}, {3585, 3632}, {3634, 3635}, + {3648, 3653}, {3654, 3654}, {3713, 3714}, {3716, 3716}, + {3718, 3722}, {3724, 3747}, {3749, 3749}, {3751, 3760}, + {3762, 3763}, {3773, 3773}, {3776, 3780}, {3782, 3782}, + {3804, 3807}, {3840, 3840}, {3904, 3911}, {3913, 3948}, + {3976, 3980}, {4096, 4138}, {4159, 4159}, {4176, 4181}, + {4186, 4189}, {4193, 4193}, {4197, 4198}, {4206, 4208}, + {4213, 4225}, {4238, 4238}, {4256, 4293}, {4295, 4295}, + {4301, 4301}, {4304, 4346}, {4348, 4348}, {4349, 4351}, + {4352, 4680}, {4682, 4685}, {4688, 4694}, {4696, 4696}, + {4698, 4701}, {4704, 4744}, {4746, 4749}, {4752, 4784}, + {4786, 4789}, {4792, 4798}, {4800, 4800}, {4802, 4805}, + {4808, 4822}, {4824, 4880}, {4882, 4885}, {4888, 4954}, + {4992, 5007}, {5024, 5109}, {5112, 5117}, {5121, 5740}, + {5743, 5759}, {5761, 5786}, {5792, 5866}, {5870, 5872}, + {5873, 5880}, {5888, 5905}, {5919, 5937}, {5952, 5969}, + {5984, 5996}, {5998, 6000}, {6016, 6067}, {6103, 6103}, + {6108, 6108}, {6176, 6210}, {6211, 6211}, {6212, 6264}, + {6272, 6276}, {6277, 6278}, {6279, 6312}, {6314, 6314}, + {6320, 6389}, {6400, 6430}, {6480, 6509}, {6512, 6516}, + {6528, 6571}, {6576, 6601}, {6656, 6678}, {6688, 6740}, + {6823, 6823}, {6917, 6963}, {6981, 6988}, {7043, 7072}, + {7086, 7087}, {7098, 7141}, {7168, 7203}, {7245, 7247}, + {7258, 7287}, {7288, 7293}, {7296, 7304}, {7312, 7354}, + {7357, 7359}, {7401, 7404}, {7406, 7411}, {7413, 7414}, + {7418, 7418}, {7424, 7467}, {7468, 7530}, {7531, 7543}, + {7544, 7544}, {7545, 7578}, {7579, 7615}, {7680, 7957}, + {7960, 7965}, {7968, 8005}, {8008, 8013}, {8016, 8023}, + {8025, 8025}, {8027, 8027}, {8029, 8029}, {8031, 8061}, + {8064, 8116}, {8118, 8124}, {8126, 8126}, {8130, 8132}, + {8134, 8140}, {8144, 8147}, {8150, 8155}, {8160, 8172}, + {8178, 8180}, {8182, 8188}, {8305, 8305}, {8319, 8319}, + {8336, 8348}, {8450, 8450}, {8455, 8455}, {8458, 8467}, + {8469, 8469}, {8472, 8472}, {8473, 8477}, {8484, 8484}, + {8486, 8486}, {8488, 8488}, {8490, 8493}, {8494, 8494}, + {8495, 8500}, {8501, 8504}, {8505, 8505}, {8508, 8511}, + {8517, 8521}, {8526, 8526}, {8544, 8578}, {8579, 8580}, + {8581, 8584}, {11264, 11387}, {11388, 11389}, {11390, 11492}, + {11499, 11502}, {11506, 11507}, {11520, 11557}, {11559, 11559}, + {11565, 11565}, {11568, 11623}, {11631, 11631}, {11648, 11670}, + {11680, 11686}, {11688, 11694}, {11696, 11702}, {11704, 11710}, + {11712, 11718}, {11720, 11726}, {11728, 11734}, {11736, 11742}, + {12293, 12293}, {12294, 12294}, {12295, 12295}, {12321, 12329}, + {12337, 12341}, {12344, 12346}, {12347, 12347}, {12348, 12348}, + {12353, 12438}, {12443, 12444}, {12445, 12446}, {12447, 12447}, + {12449, 12538}, {12540, 12542}, {12543, 12543}, {12549, 12591}, + {12593, 12686}, {12704, 12735}, {12784, 12799}, {13312, 19903}, + {19968, 40980}, {40981, 40981}, {40982, 42124}, {42192, 42231}, + {42232, 42237}, {42240, 42507}, {42508, 42508}, {42512, 42527}, + {42538, 42539}, {42560, 42605}, {42606, 42606}, {42623, 42623}, + {42624, 42651}, {42652, 42653}, {42656, 42725}, {42726, 42735}, + {42775, 42783}, {42786, 42863}, {42864, 42864}, {42865, 42887}, + {42888, 42888}, {42891, 42894}, {42895, 42895}, {42896, 42954}, + {42960, 42961}, {42963, 42963}, {42965, 42969}, {42994, 42996}, + {42997, 42998}, {42999, 42999}, {43000, 43001}, {43002, 43002}, + {43003, 43009}, {43011, 43013}, {43015, 43018}, {43020, 43042}, + {43072, 43123}, {43138, 43187}, {43250, 43255}, {43259, 43259}, + {43261, 43262}, {43274, 43301}, {43312, 43334}, {43360, 43388}, + {43396, 43442}, {43471, 43471}, {43488, 43492}, {43494, 43494}, + {43495, 43503}, {43514, 43518}, {43520, 43560}, {43584, 43586}, + {43588, 43595}, {43616, 43631}, {43632, 43632}, {43633, 43638}, + {43642, 43642}, {43646, 43695}, {43697, 43697}, {43701, 43702}, + {43705, 43709}, {43712, 43712}, {43714, 43714}, {43739, 43740}, + {43741, 43741}, {43744, 43754}, {43762, 43762}, {43763, 43764}, + {43777, 43782}, {43785, 43790}, {43793, 43798}, {43808, 43814}, + {43816, 43822}, {43824, 43866}, {43868, 43871}, {43872, 43880}, + {43881, 43881}, {43888, 43967}, {43968, 44002}, {44032, 55203}, + {55216, 55238}, {55243, 55291}, {63744, 64109}, {64112, 64217}, + {64256, 64262}, {64275, 64279}, {64285, 64285}, {64287, 64296}, + {64298, 64310}, {64312, 64316}, {64318, 64318}, {64320, 64321}, + {64323, 64324}, {64326, 64433}, {64467, 64829}, {64848, 64911}, + {64914, 64967}, {65008, 65019}, {65136, 65140}, {65142, 65276}, + {65313, 65338}, {65345, 65370}, {65382, 65391}, {65392, 65392}, + {65393, 65437}, {65438, 65439}, {65440, 65470}, {65474, 65479}, + {65482, 65487}, {65490, 65495}, {65498, 65500}, {65536, 65547}, + {65549, 65574}, {65576, 65594}, {65596, 65597}, {65599, 65613}, + {65616, 65629}, {65664, 65786}, {65856, 65908}, {66176, 66204}, + {66208, 66256}, {66304, 66335}, {66349, 66368}, {66369, 66369}, + {66370, 66377}, {66378, 66378}, {66384, 66421}, {66432, 66461}, + {66464, 66499}, {66504, 66511}, {66513, 66517}, {66560, 66639}, + {66640, 66717}, {66736, 66771}, {66776, 66811}, {66816, 66855}, + {66864, 66915}, {66928, 66938}, {66940, 66954}, {66956, 66962}, + {66964, 66965}, {66967, 66977}, {66979, 66993}, {66995, 67001}, + {67003, 67004}, {67072, 67382}, {67392, 67413}, {67424, 67431}, + {67456, 67461}, {67463, 67504}, {67506, 67514}, {67584, 67589}, + {67592, 67592}, {67594, 67637}, {67639, 67640}, {67644, 67644}, + {67647, 67669}, {67680, 67702}, {67712, 67742}, {67808, 67826}, + {67828, 67829}, {67840, 67861}, {67872, 67897}, {67968, 68023}, + {68030, 68031}, {68096, 68096}, {68112, 68115}, {68117, 68119}, + {68121, 68149}, {68192, 68220}, {68224, 68252}, {68288, 68295}, + {68297, 68324}, {68352, 68405}, {68416, 68437}, {68448, 68466}, + {68480, 68497}, {68608, 68680}, {68736, 68786}, {68800, 68850}, + {68864, 68899}, {69248, 69289}, {69296, 69297}, {69376, 69404}, + {69415, 69415}, {69424, 69445}, {69488, 69505}, {69552, 69572}, + {69600, 69622}, {69635, 69687}, {69745, 69746}, {69749, 69749}, + {69763, 69807}, {69840, 69864}, {69891, 69926}, {69956, 69956}, + {69959, 69959}, {69968, 70002}, {70006, 70006}, {70019, 70066}, + {70081, 70084}, {70106, 70106}, {70108, 70108}, {70144, 70161}, + {70163, 70187}, {70207, 70208}, {70272, 70278}, {70280, 70280}, + {70282, 70285}, {70287, 70301}, {70303, 70312}, {70320, 70366}, + {70405, 70412}, {70415, 70416}, {70419, 70440}, {70442, 70448}, + {70450, 70451}, {70453, 70457}, {70461, 70461}, {70480, 70480}, + {70493, 70497}, {70656, 70708}, {70727, 70730}, {70751, 70753}, + {70784, 70831}, {70852, 70853}, {70855, 70855}, {71040, 71086}, + {71128, 71131}, {71168, 71215}, {71236, 71236}, {71296, 71338}, + {71352, 71352}, {71424, 71450}, {71488, 71494}, {71680, 71723}, + {71840, 71903}, {71935, 71942}, {71945, 71945}, {71948, 71955}, + {71957, 71958}, {71960, 71983}, {71999, 71999}, {72001, 72001}, + {72096, 72103}, {72106, 72144}, {72161, 72161}, {72163, 72163}, + {72192, 72192}, {72203, 72242}, {72250, 72250}, {72272, 72272}, + {72284, 72329}, {72349, 72349}, {72368, 72440}, {72704, 72712}, + {72714, 72750}, {72768, 72768}, {72818, 72847}, {72960, 72966}, + {72968, 72969}, {72971, 73008}, {73030, 73030}, {73056, 73061}, + {73063, 73064}, {73066, 73097}, {73112, 73112}, {73440, 73458}, + {73474, 73474}, {73476, 73488}, {73490, 73523}, {73648, 73648}, + {73728, 74649}, {74752, 74862}, {74880, 75075}, {77712, 77808}, + {77824, 78895}, {78913, 78918}, {82944, 83526}, {92160, 92728}, + {92736, 92766}, {92784, 92862}, {92880, 92909}, {92928, 92975}, + {92992, 92995}, {93027, 93047}, {93053, 93071}, {93760, 93823}, + {93952, 94026}, {94032, 94032}, {94099, 94111}, {94176, 94177}, + {94179, 94179}, {94208, 100343}, {100352, 101589}, {101632, 101640}, + {110576, 110579}, {110581, 110587}, {110589, 110590}, {110592, 110882}, + {110898, 110898}, {110928, 110930}, {110933, 110933}, {110948, 110951}, + {110960, 111355}, {113664, 113770}, {113776, 113788}, {113792, 113800}, + {113808, 113817}, {119808, 119892}, {119894, 119964}, {119966, 119967}, + {119970, 119970}, {119973, 119974}, {119977, 119980}, {119982, 119993}, + {119995, 119995}, {119997, 120003}, {120005, 120069}, {120071, 120074}, + {120077, 120084}, {120086, 120092}, {120094, 120121}, {120123, 120126}, + {120128, 120132}, {120134, 120134}, {120138, 120144}, {120146, 120485}, + {120488, 120512}, {120514, 120538}, {120540, 120570}, {120572, 120596}, + {120598, 120628}, {120630, 120654}, {120656, 120686}, {120688, 120712}, + {120714, 120744}, {120746, 120770}, {120772, 120779}, {122624, 122633}, + {122634, 122634}, {122635, 122654}, {122661, 122666}, {122928, 122989}, + {123136, 123180}, {123191, 123197}, {123214, 123214}, {123536, 123565}, + {123584, 123627}, {124112, 124138}, {124139, 124139}, {124896, 124902}, + {124904, 124907}, {124909, 124910}, {124912, 124926}, {124928, 125124}, + {125184, 125251}, {125259, 125259}, {126464, 126467}, {126469, 126495}, + {126497, 126498}, {126500, 126500}, {126503, 126503}, {126505, 126514}, + {126516, 126519}, {126521, 126521}, {126523, 126523}, {126530, 126530}, + {126535, 126535}, {126537, 126537}, {126539, 126539}, {126541, 126543}, + {126545, 126546}, {126548, 126548}, {126551, 126551}, {126553, 126553}, + {126555, 126555}, {126557, 126557}, {126559, 126559}, {126561, 126562}, + {126564, 126564}, {126567, 126570}, {126572, 126578}, {126580, 126583}, + {126585, 126588}, {126590, 126590}, {126592, 126601}, {126603, 126619}, + {126625, 126627}, {126629, 126633}, {126635, 126651}, {131072, 173791}, + {173824, 177977}, {177984, 178205}, {178208, 183969}, {183984, 191456}, + {191472, 192093}, {194560, 195101}, {196608, 201546}, {201552, 205743} +}; + + +} // namespace ada::idna +#endif // ADA_IDNA_IDENTIFIER_TABLES_H + +/* end file src/id_tables.cpp */ + +namespace ada::idna { +// return 0xffffffff in case of error +// We do not fully validate the input +uint32_t get_first_code_point(std::string_view input) { + constexpr uint32_t error = 0xffffffff; + // Check if the input is empty + if (input.empty()) { + return error; + } + + uint32_t code_point = 0; + size_t number_bytes = 0; + unsigned char first_byte = input[0]; + + if ((first_byte & 0x80) == 0) { + // 1-byte character (ASCII) + return first_byte; + } else if ((first_byte & 0xE0) == 0xC0) { + // 2-byte character + code_point = first_byte & 0x1F; + number_bytes = 2; + } else if ((first_byte & 0xF0) == 0xE0) { + // 3-byte character + code_point = first_byte & 0x0F; + number_bytes = 3; + } else if ((first_byte & 0xF8) == 0xF0) { + // 4-byte character + code_point = first_byte & 0x07; + number_bytes = 4; + } else { + return error; + } + + // Decode the remaining bytes + for (size_t i = 1; i < number_bytes; ++i) { + if (i >= input.size()) { + return error; + } + unsigned char byte = input[i]; + if ((byte & 0xC0) != 0x80) { + return error; + } + code_point = (code_point << 6) | (byte & 0x3F); + } + return code_point; +} + +bool is_ascii_letter(char32_t c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +bool is_ascii_letter_or_digit(char32_t c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9'); +} + +bool valid_name_code_point(char32_t code_point, bool first) { + // https://tc39.es/ecma262/#prod-IdentifierStart + // Fast paths: + if (first && + (code_point == '$' || code_point == '_' || is_ascii_letter(code_point))) { + return true; + } + if (!first && (code_point == '$' || is_ascii_letter_or_digit(code_point))) { + return true; + } + // Slow path... + if (code_point == 0xffffffff) { + return false; // minimal error handling + } + if (first) { + auto iter = std::lower_bound( + std::begin(ada::idna::id_start), std::end(ada::idna::id_start), + code_point, [](const uint32_t* range, uint32_t code_point) { + return range[1] < code_point; + }); + return iter != std::end(id_start) && code_point >= (*iter)[0]; + } else { + auto iter = std::lower_bound( + std::begin(id_continue), std::end(id_continue), code_point, + [](const uint32_t* range, uint32_t code_point) { + return range[1] < code_point; + }); + return iter != std::end(id_start) && code_point >= (*iter)[0]; + } +} +} // namespace ada::idna +/* end file src/identifier.cpp */ /* end file src/idna.cpp */ /* end file src/ada_idna.cpp */ ADA_POP_DISABLE_WARNINGS @@ -9935,7 +10557,7 @@ ada_really_inline bool has_tabs_or_newline( // U+003F (?), U+0040 (@), U+005B ([), U+005C (\), U+005D (]), U+005E (^), or // U+007C (|). constexpr static std::array is_forbidden_host_code_point_table = - []() constexpr { + []() consteval { std::array result{}; for (uint8_t c : {'\0', '\x09', '\x0a', '\x0d', ' ', '#', '/', ':', '<', '>', '?', '@', '[', '\\', ']', '^', '|'}) { @@ -9950,7 +10572,7 @@ ada_really_inline constexpr bool is_forbidden_host_code_point( } constexpr static std::array is_forbidden_domain_code_point_table = - []() constexpr { + []() consteval { std::array result{}; for (uint8_t c : {'\0', '\x09', '\x0a', '\x0d', ' ', '#', '/', ':', '<', '>', '?', '@', '[', '\\', ']', '^', '|', '%'}) { @@ -9989,7 +10611,7 @@ ada_really_inline constexpr bool contains_forbidden_domain_code_point( } constexpr static std::array - is_forbidden_domain_code_point_table_or_upper = []() constexpr { + is_forbidden_domain_code_point_table_or_upper = []() consteval { std::array result{}; for (uint8_t c : {'\0', '\x09', '\x0a', '\x0d', ' ', '#', '/', ':', '<', '>', '?', '@', '[', '\\', ']', '^', '|', '%'}) { @@ -10030,7 +10652,7 @@ contains_forbidden_domain_code_point_or_upper(const char* input, } // std::isalnum(c) || c == '+' || c == '-' || c == '.') is true for -constexpr static std::array is_alnum_plus_table = []() constexpr { +constexpr static std::array is_alnum_plus_table = []() consteval { std::array result{}; for (size_t c = 0; c < 256; c++) { result[c] = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || @@ -10051,6 +10673,17 @@ ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept { (c >= 'a' && c <= 'f'); } +ada_really_inline constexpr bool is_ascii_digit(const char c) noexcept { + // An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9), + // inclusive. + return (c >= '0' && c <= '9'); +} + +ada_really_inline constexpr bool is_ascii(const char32_t c) noexcept { + // If code point is between U+0000 and U+007F inclusive, then return true. + return c <= 0x7F; +} + ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept { return (unsigned char)c <= ' '; } @@ -10063,7 +10696,7 @@ ada_really_inline constexpr bool is_ascii_tab_or_newline( constexpr std::string_view table_is_double_dot_path_segment[] = { "..", "%2e.", ".%2e", "%2e%2e"}; -ada_really_inline ada_constexpr bool is_double_dot_path_segment( +ada_really_inline constexpr bool is_double_dot_path_segment( std::string_view input) noexcept { // This will catch most cases: // The length must be 2,4 or 6. @@ -10151,7 +10784,6 @@ std::string percent_decode(const std::string_view input, size_t first_percent) { !is_ascii_hex_digit(pointer[2])))) { dest += ch; pointer++; - continue; } else { unsigned a = convert_hex_to_binary(pointer[1]); unsigned b = convert_hex_to_binary(pointer[2]); @@ -10165,10 +10797,9 @@ std::string percent_decode(const std::string_view input, size_t first_percent) { std::string percent_encode(const std::string_view input, const uint8_t character_set[]) { - auto pointer = - std::find_if(input.begin(), input.end(), [character_set](const char c) { - return character_sets::bit_at(character_set, c); - }); + auto pointer = std::ranges::find_if(input, [character_set](const char c) { + return character_sets::bit_at(character_set, c); + }); // Optimization: Don't iterate if percent encode is not required if (pointer == input.end()) { return std::string(input); @@ -10207,7 +10838,7 @@ bool percent_encode(const std::string_view input, const uint8_t character_set[], ada_log("percent_encode encoding not needed."); return false; } - if (!append) { + if constexpr (!append) { out.clear(); } ada_log("percent_encode appending ", std::distance(input.begin(), pointer), @@ -10261,8 +10892,8 @@ std::string percent_encode(const std::string_view input, } // namespace ada::unicode /* end file src/unicode.cpp */ /* begin file src/serializers.cpp */ - #include +#include #include namespace ada::serializers { @@ -10342,18 +10973,19 @@ std::string ipv4(const uint64_t address) noexcept { } // namespace ada::serializers /* end file src/serializers.cpp */ /* begin file src/implementation.cpp */ + #include namespace ada { template -ada_warn_unused tl::expected parse( +ada_warn_unused tl::expected parse( std::string_view input, const result_type* base_url) { result_type u = ada::parser::parse_url_impl(input, base_url); if (!u.is_valid) { - return tl::unexpected(errors::generic_error); + return tl::unexpected(errors::type_error); } return u; } @@ -10422,8 +11054,6 @@ ada_warn_unused std::string to_string(ada::encoding_type type) { /* end file src/implementation.cpp */ /* begin file src/helpers.cpp */ -#include -#include #include #include @@ -10517,13 +11147,11 @@ ada_really_inline std::optional prune_hash( ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type) noexcept { - size_t first_delimiter = path.find_first_of('/', 1); - // Let path be url's path. // If url's scheme is "file", path's size is 1, and path[0] is a normalized // Windows drive letter, then return. if (type == ada::scheme::type::FILE && - first_delimiter == std::string_view::npos && !path.empty()) { + path.find('/', 1) == std::string_view::npos && !path.empty()) { if (checkers::is_normalized_windows_drive_letter( helpers::substring(path, 1))) { return false; @@ -10542,13 +11170,11 @@ ada_really_inline bool shorten_path(std::string& path, ada_really_inline bool shorten_path(std::string_view& path, ada::scheme::type type) noexcept { - size_t first_delimiter = path.find_first_of('/', 1); - // Let path be url's path. // If url's scheme is "file", path's size is 1, and path[0] is a normalized // Windows drive letter, then return. if (type == ada::scheme::type::FILE && - first_delimiter == std::string_view::npos && !path.empty()) { + path.find('/', 1) == std::string_view::npos && !path.empty()) { if (checkers::is_normalized_windows_drive_letter( helpers::substring(path, 1))) { return false; @@ -10571,15 +11197,11 @@ ada_really_inline void remove_ascii_tab_or_newline( std::string& input) noexcept { // if this ever becomes a performance issue, we could use an approach similar // to has_tabs_or_newline - input.erase(std::remove_if(input.begin(), input.end(), - [](char c) { - return ada::unicode::is_ascii_tab_or_newline(c); - }), - input.end()); + std::erase_if(input, ada::unicode::is_ascii_tab_or_newline); } -ada_really_inline std::string_view substring(std::string_view input, - size_t pos) noexcept { +ada_really_inline constexpr std::string_view substring(std::string_view input, + size_t pos) noexcept { ADA_ASSERT_TRUE(pos <= input.size()); // The following is safer but unneeded if we have the above line: // return pos > input.size() ? std::string_view() : input.substr(pos); @@ -10736,7 +11358,7 @@ ada_really_inline size_t find_next_host_delimiter_special( #else // : / [ \\ ? static constexpr std::array special_host_delimiters = - []() constexpr { + []() consteval { std::array result{}; for (int i : {':', '/', '[', '\\', '?'}) { result[i] = 1; @@ -10868,7 +11490,7 @@ ada_really_inline size_t find_next_host_delimiter(std::string_view view, } #else // : / [ ? -static constexpr std::array host_delimiters = []() constexpr { +static constexpr std::array host_delimiters = []() consteval { std::array result{}; for (int i : {':', '/', '?', '['}) { result[i] = 1; @@ -10968,7 +11590,7 @@ ada_really_inline std::pair get_host_delimiter_location( return {location, found_colon}; } -ada_really_inline void trim_c0_whitespace(std::string_view& input) noexcept { +void trim_c0_whitespace(std::string_view& input) noexcept { while (!input.empty() && ada::unicode::is_c0_control_or_space(input.front())) { input.remove_prefix(1); @@ -11163,7 +11785,7 @@ ada_really_inline void strip_trailing_spaces_from_opaque_path( // @ / \\ ? static constexpr std::array authority_delimiter_special = - []() constexpr { + []() consteval { std::array result{}; for (uint8_t i : {'@', '/', '\\', '?'}) { result[i] = 1; @@ -11184,7 +11806,7 @@ find_authority_delimiter_special(std::string_view view) noexcept { } // @ / ? -static constexpr std::array authority_delimiter = []() constexpr { +static constexpr std::array authority_delimiter = []() consteval { std::array result{}; for (uint8_t i : {'@', '/', '?'}) { result[i] = 1; @@ -11218,13 +11840,14 @@ ada_warn_unused std::string to_string(ada::state state) { #include #include #include +#include namespace ada { bool url::parse_opaque_host(std::string_view input) { ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]"); - if (std::any_of(input.begin(), input.end(), - ada::unicode::is_forbidden_host_code_point)) { + if (std::ranges::any_of(input.begin(), input.end(), + ada::unicode::is_forbidden_host_code_point)) { return is_valid = false; } @@ -11549,7 +12172,7 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { *http, https), in which case, we can go really fast. **/ if (is_input_special) { // fast path!!! - if (has_state_override) { + if constexpr (has_state_override) { // If url's scheme is not a special scheme and buffer is a special scheme, // then return. if (is_special() != is_input_special) { @@ -11573,7 +12196,7 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { type = parsed_type; - if (has_state_override) { + if constexpr (has_state_override) { // This is uncommon. uint16_t urls_scheme_port = get_special_port(); @@ -11593,7 +12216,7 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { // bool is_ascii = unicode::to_lower_ascii(_buffer.data(), _buffer.size()); - if (has_state_override) { + if constexpr (has_state_override) { // If url's scheme is a special scheme and buffer is not a special scheme, // then return. If url's scheme is not a special scheme and buffer is a // special scheme, then return. @@ -11617,7 +12240,7 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { set_scheme(std::move(_buffer)); - if (has_state_override) { + if constexpr (has_state_override) { // This is uncommon. uint16_t urls_scheme_port = get_special_port(); @@ -11726,18 +12349,14 @@ ada_really_inline void url::parse_path(std::string_view input) { path = "/"; } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) { helpers::parse_prepared_path(internal_input.substr(1), type, path); - return; } else { helpers::parse_prepared_path(internal_input, type, path); - return; } } else if (!internal_input.empty()) { if (internal_input[0] == '/') { helpers::parse_prepared_path(internal_input.substr(1), type, path); - return; } else { helpers::parse_prepared_path(internal_input, type, path); - return; } } else { if (!host.has_value()) { @@ -11802,17 +12421,6 @@ ada_really_inline void url::parse_path(std::string_view input) { return checkers::verify_dns_length(host.value()); } -} // namespace ada -/* end file src/url.cpp */ -/* begin file src/url-getters.cpp */ -/** - * @file url-getters.cpp - * Includes all the getters of `ada::url` - */ - -#include - -namespace ada { [[nodiscard]] std::string url::get_origin() const noexcept { if (is_special()) { // Return a new opaque origin. @@ -11865,10 +12473,6 @@ namespace ada { return host.value_or(""); } -[[nodiscard]] std::string_view url::get_pathname() const noexcept { - return path; -} - [[nodiscard]] std::string url::get_search() const noexcept { // If this's URL's query is either null or the empty string, then return the // empty string. Return U+003F (?), followed by this's URL's query. @@ -11895,19 +12499,6 @@ namespace ada { : "#" + hash.value(); } -} // namespace ada -/* end file src/url-getters.cpp */ -/* begin file src/url-setters.cpp */ -/** - * @file url-setters.cpp - * Includes all the setters of `ada::url` - */ - -#include -#include - -namespace ada { - template bool url::set_host_or_hostname(const std::string_view input) { if (has_opaque_path) { @@ -11935,7 +12526,7 @@ bool url::set_host_or_hostname(const std::string_view input) { // Note: the 'found_colon' value is true if and only if a colon was // encountered while not inside brackets. if (found_colon) { - if (override_hostname) { + if constexpr (override_hostname) { return false; } std::string_view buffer = new_host.substr(location + 1); @@ -11960,7 +12551,7 @@ bool url::set_host_or_hostname(const std::string_view input) { bool succeeded = parse_host(host_view); if (!succeeded) { - host = previous_host; + host = std::move(previous_host); update_base_port(previous_port); } return succeeded; @@ -11977,13 +12568,13 @@ bool url::set_host_or_hostname(const std::string_view input) { } else { // Let host be the result of host parsing buffer with url is not special. if (!parse_host(new_host)) { - host = previous_host; + host = std::move(previous_host); update_base_port(previous_port); return false; } // If host is "localhost", then set host to the empty string. - if (host.has_value() && host.value() == "localhost") { + if (host == "localhost") { host = ""; } } @@ -12026,12 +12617,9 @@ bool url::set_port(const std::string_view input) { port = std::nullopt; return true; } - // Input should not start with control characters. - if (ada::unicode::is_c0_control_or_space(trimmed.front())) { - return false; - } - // Input should contain at least one ascii digit. - if (input.find_first_of("0123456789") == std::string_view::npos) { + + // Input should not start with a non-digit character. + if (!ada::unicode::is_ascii_digit(trimmed.front())) { return false; } @@ -12041,7 +12629,7 @@ bool url::set_port(const std::string_view input) { if (is_valid) { return true; } - port = previous_port; + port = std::move(previous_port); is_valid = true; return false; } @@ -12075,15 +12663,14 @@ void url::set_search(const std::string_view input) { is_special() ? ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE : ada::character_sets::QUERY_PERCENT_ENCODE; - query = ada::unicode::percent_encode(std::string_view(new_value), - query_percent_encode_set); + query = ada::unicode::percent_encode(new_value, query_percent_encode_set); } bool url::set_pathname(const std::string_view input) { if (has_opaque_path) { return false; } - path = ""; + path.clear(); parse_path(input); return true; } @@ -12103,7 +12690,7 @@ bool url::set_protocol(const std::string_view input) { view.append(":"); std::string::iterator pointer = - std::find_if_not(view.begin(), view.end(), unicode::is_alnum_plus); + std::ranges::find_if_not(view, unicode::is_alnum_plus); if (pointer != view.end() && *pointer == ':') { return parse_scheme( @@ -12116,23 +12703,14 @@ bool url::set_href(const std::string_view input) { ada::result out = ada::parse(input); if (out) { - username = out->username; - password = out->password; - host = out->host; - port = out->port; - path = out->path; - query = out->query; - hash = out->hash; - type = out->type; - non_special_scheme = out->non_special_scheme; - has_opaque_path = out->has_opaque_path; + *this = *out; } return out.has_value(); } } // namespace ada -/* end file src/url-setters.cpp */ +/* end file src/url.cpp */ /* begin file src/parser.cpp */ #include @@ -12148,10 +12726,9 @@ result_type parse_url_impl(std::string_view user_input, // means that doing if constexpr(result_type_is_ada_url) { something } else { // something else } is free (at runtime). This means that ada::url_aggregator // and ada::url **do not have to support the exact same API**. - constexpr bool result_type_is_ada_url = - std::is_same::value; + constexpr bool result_type_is_ada_url = std::is_same_v; constexpr bool result_type_is_ada_url_aggregator = - std::is_same::value; + std::is_same_v; static_assert(result_type_is_ada_url || result_type_is_ada_url_aggregator); // We don't support // anything else for now. @@ -12160,12 +12737,12 @@ result_type parse_url_impl(std::string_view user_input, " bytes],", (base_url != nullptr ? base_url->to_string() : "null"), ")"); - ada::state state = ada::state::SCHEME_START; + state state = state::SCHEME_START; result_type url{}; // We refuse to parse URL strings that exceed 4GB. Such strings are almost // surely the result of a bug or are otherwise a security concern. - if (user_input.size() > std::numeric_limits::max()) { + if (user_input.size() > std::numeric_limits::max()) [[unlikely]] { url.is_valid = false; } // Going forward, user_input.size() is in [0, @@ -12196,20 +12773,19 @@ result_type parse_url_impl(std::string_view user_input, url.reserve(reserve_capacity); } std::string tmp_buffer; - std::string_view internal_input; - if (unicode::has_tabs_or_newline(user_input)) { + std::string_view url_data; + if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] { tmp_buffer = user_input; // Optimization opportunity: Instead of copying and then pruning, we could // just directly build the string from user_input. helpers::remove_ascii_tab_or_newline(tmp_buffer); - internal_input = tmp_buffer; - } else { - internal_input = user_input; + url_data = tmp_buffer; + } else [[likely]] { + url_data = user_input; } // Leading and trailing control characters are uncommon and easy to deal with // (no performance concern). - std::string_view url_data = internal_input; helpers::trim_c0_whitespace(url_data); // Optimization opportunity. Most websites do not have fragment. @@ -12232,27 +12808,27 @@ result_type parse_url_impl(std::string_view user_input, ada_log("In parsing at ", input_position, " out of ", input_size, " in state ", ada::to_string(state)); switch (state) { - case ada::state::SCHEME_START: { + case state::SCHEME_START: { ada_log("SCHEME_START ", helpers::substring(url_data, input_position)); // If c is an ASCII alpha, append c, lowercased, to buffer, and set // state to scheme state. if ((input_position != input_size) && checkers::is_alpha(url_data[input_position])) { - state = ada::state::SCHEME; + state = state::SCHEME; input_position++; } else { // Otherwise, if state override is not given, set state to no scheme // state and decrease pointer by 1. - state = ada::state::NO_SCHEME; + state = state::NO_SCHEME; } break; } - case ada::state::SCHEME: { + case state::SCHEME: { ada_log("SCHEME ", helpers::substring(url_data, input_position)); // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.), // append c, lowercased, to buffer. while ((input_position != input_size) && - (ada::unicode::is_alnum_plus(url_data[input_position]))) { + (unicode::is_alnum_plus(url_data[input_position]))) { input_position++; } // Otherwise, if c is U+003A (:), then: @@ -12274,9 +12850,9 @@ result_type parse_url_impl(std::string_view user_input, ada_log("SCHEME the scheme is ", url.get_protocol()); // If url's scheme is "file", then: - if (url.type == ada::scheme::type::FILE) { + if (url.type == scheme::type::FILE) { // Set state to file state. - state = ada::state::FILE; + state = state::FILE; } // Otherwise, if url is special, base is non-null, and base's scheme // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url @@ -12284,38 +12860,38 @@ result_type parse_url_impl(std::string_view user_input, else if (url.is_special() && base_url != nullptr && base_url->type == url.type) { // Set state to special relative or authority state. - state = ada::state::SPECIAL_RELATIVE_OR_AUTHORITY; + state = state::SPECIAL_RELATIVE_OR_AUTHORITY; } // Otherwise, if url is special, set state to special authority // slashes state. else if (url.is_special()) { - state = ada::state::SPECIAL_AUTHORITY_SLASHES; + state = state::SPECIAL_AUTHORITY_SLASHES; } // Otherwise, if remaining starts with an U+002F (/), set state to // path or authority state and increase pointer by 1. else if (input_position + 1 < input_size && url_data[input_position + 1] == '/') { - state = ada::state::PATH_OR_AUTHORITY; + state = state::PATH_OR_AUTHORITY; input_position++; } // Otherwise, set url's path to the empty string and set state to // opaque path state. else { - state = ada::state::OPAQUE_PATH; + state = state::OPAQUE_PATH; } } // Otherwise, if state override is not given, set buffer to the empty // string, state to no scheme state, and start over (from the first code // point in input). else { - state = ada::state::NO_SCHEME; + state = state::NO_SCHEME; input_position = 0; break; } input_position++; break; } - case ada::state::NO_SCHEME: { + case state::NO_SCHEME: { ada_log("NO_SCHEME ", helpers::substring(url_data, input_position)); // If base is null, or base has an opaque path and c is not U+0023 (#), // validation error, return failure. @@ -12346,18 +12922,18 @@ result_type parse_url_impl(std::string_view user_input, } // Otherwise, if base's scheme is not "file", set state to relative // state and decrease pointer by 1. - else if (base_url->type != ada::scheme::type::FILE) { + else if (base_url->type != scheme::type::FILE) { ada_log("NO_SCHEME non-file relative path"); - state = ada::state::RELATIVE_SCHEME; + state = state::RELATIVE_SCHEME; } // Otherwise, set state to file state and decrease pointer by 1. else { ada_log("NO_SCHEME file base type"); - state = ada::state::FILE; + state = state::FILE; } break; } - case ada::state::AUTHORITY: { + case state::AUTHORITY: { ada_log("AUTHORITY ", helpers::substring(url_data, input_position)); // most URLs have no @. Having no @ tells us that we don't have to worry // about AUTHORITY. Of course, we could have @ and still not have to @@ -12367,11 +12943,10 @@ result_type parse_url_impl(std::string_view user_input, // TODO: We could do various processing early on, using a single pass // over the string to collect information about it, e.g., telling us // whether there is a @ and if so, where (or how many). - const bool contains_ampersand = - (url_data.find('@', input_position) != std::string_view::npos); - if (!contains_ampersand) { - state = ada::state::HOST; + // Check if url data contains an @. + if (url_data.find('@', input_position) == std::string_view::npos) { + state = state::HOST; break; } bool at_sign_seen{false}; @@ -12382,12 +12957,12 @@ result_type parse_url_impl(std::string_view user_input, * --------^ */ do { - std::string_view view = helpers::substring(url_data, input_position); + std::string_view view = url_data.substr(input_position); // The delimiters are @, /, ? \\. size_t location = url.is_special() ? helpers::find_authority_delimiter_special(view) : helpers::find_authority_delimiter(view); - std::string_view authority_view(view.data(), location); + std::string_view authority_view = view.substr(0, location); size_t end_of_authority = input_position + authority_view.size(); // If c is U+0040 (@), then: if ((end_of_authority != input_size) && @@ -12468,7 +13043,7 @@ result_type parse_url_impl(std::string_view user_input, url.is_valid = false; return url; } - state = ada::state::HOST; + state = state::HOST; break; } if (end_of_authority == input_size) { @@ -12484,42 +13059,41 @@ result_type parse_url_impl(std::string_view user_input, break; } - case ada::state::SPECIAL_RELATIVE_OR_AUTHORITY: { + case state::SPECIAL_RELATIVE_OR_AUTHORITY: { ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ", helpers::substring(url_data, input_position)); // If c is U+002F (/) and remaining starts with U+002F (/), // then set state to special authority ignore slashes state and increase // pointer by 1. - std::string_view view = helpers::substring(url_data, input_position); - if (ada::checkers::begins_with(view, "//")) { - state = ada::state::SPECIAL_AUTHORITY_IGNORE_SLASHES; + if (url_data.substr(input_position, 2) == "//") { + state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES; input_position += 2; } else { // Otherwise, validation error, set state to relative state and // decrease pointer by 1. - state = ada::state::RELATIVE_SCHEME; + state = state::RELATIVE_SCHEME; } break; } - case ada::state::PATH_OR_AUTHORITY: { + case state::PATH_OR_AUTHORITY: { ada_log("PATH_OR_AUTHORITY ", helpers::substring(url_data, input_position)); // If c is U+002F (/), then set state to authority state. if ((input_position != input_size) && (url_data[input_position] == '/')) { - state = ada::state::AUTHORITY; + state = state::AUTHORITY; input_position++; } else { // Otherwise, set state to path state, and decrease pointer by 1. - state = ada::state::PATH; + state = state::PATH; } break; } - case ada::state::RELATIVE_SCHEME: { + case state::RELATIVE_SCHEME: { ada_log("RELATIVE_SCHEME ", helpers::substring(url_data, input_position)); @@ -12532,7 +13106,7 @@ result_type parse_url_impl(std::string_view user_input, ada_log( "RELATIVE_SCHEME if c is U+002F (/), then set state to relative " "slash state"); - state = ada::state::RELATIVE_SLASH; + state = state::RELATIVE_SLASH; } else if (url.is_special() && (input_position != input_size) && (url_data[input_position] == '\\')) { // Otherwise, if url is special and c is U+005C (\), validation error, @@ -12540,7 +13114,7 @@ result_type parse_url_impl(std::string_view user_input, ada_log( "RELATIVE_SCHEME if url is special and c is U+005C, validation " "error, set state to relative slash state"); - state = ada::state::RELATIVE_SLASH; + state = state::RELATIVE_SLASH; } else { ada_log("RELATIVE_SCHEME otherwise"); // Set url's username to base's username, url's password to base's @@ -12559,9 +13133,7 @@ result_type parse_url_impl(std::string_view user_input, } else { url.update_base_authority(base_url->get_href(), base_url->get_components()); - // TODO: Get rid of set_hostname and replace it with - // update_base_hostname - url.set_hostname(base_url->get_hostname()); + url.update_host_to_base_host(base_url->get_hostname()); url.update_base_port(base_url->retrieve_base_port()); // cloning the base path includes cloning the has_opaque_path flag url.has_opaque_path = base_url->has_opaque_path; @@ -12575,7 +13147,7 @@ result_type parse_url_impl(std::string_view user_input, // state to query state. if ((input_position != input_size) && (url_data[input_position] == '?')) { - state = ada::state::QUERY; + state = state::QUERY; } // Otherwise, if c is not the EOF code point: else if (input_position != input_size) { @@ -12587,18 +13159,18 @@ result_type parse_url_impl(std::string_view user_input, } else { std::string_view path = url.get_pathname(); if (helpers::shorten_path(path, url.type)) { - url.update_base_pathname(std::string(path)); + url.update_base_pathname(std::move(std::string(path))); } } // Set state to path state and decrease pointer by 1. - state = ada::state::PATH; + state = state::PATH; break; } } input_position++; break; } - case ada::state::RELATIVE_SLASH: { + case state::RELATIVE_SLASH: { ada_log("RELATIVE_SLASH ", helpers::substring(url_data, input_position)); @@ -12607,12 +13179,12 @@ result_type parse_url_impl(std::string_view user_input, (url_data[input_position] == '/' || url_data[input_position] == '\\')) { // Set state to special authority ignore slashes state. - state = ada::state::SPECIAL_AUTHORITY_IGNORE_SLASHES; + state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES; } // Otherwise, if c is U+002F (/), then set state to authority state. else if ((input_position != input_size) && (url_data[input_position] == '/')) { - state = ada::state::AUTHORITY; + state = state::AUTHORITY; } // Otherwise, set // - url's username to base's username, @@ -12629,33 +13201,30 @@ result_type parse_url_impl(std::string_view user_input, } else { url.update_base_authority(base_url->get_href(), base_url->get_components()); - // TODO: Get rid of set_hostname and replace it with - // update_base_hostname - url.set_hostname(base_url->get_hostname()); + url.update_host_to_base_host(base_url->get_hostname()); url.update_base_port(base_url->retrieve_base_port()); } - state = ada::state::PATH; + state = state::PATH; break; } input_position++; break; } - case ada::state::SPECIAL_AUTHORITY_SLASHES: { + case state::SPECIAL_AUTHORITY_SLASHES: { ada_log("SPECIAL_AUTHORITY_SLASHES ", helpers::substring(url_data, input_position)); // If c is U+002F (/) and remaining starts with U+002F (/), // then set state to special authority ignore slashes state and increase // pointer by 1. - std::string_view view = helpers::substring(url_data, input_position); - if (ada::checkers::begins_with(view, "//")) { + if (url_data.substr(input_position, 2) == "//") { input_position += 2; } [[fallthrough]]; } - case ada::state::SPECIAL_AUTHORITY_IGNORE_SLASHES: { + case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: { ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ", helpers::substring(url_data, input_position)); @@ -12666,23 +13235,22 @@ result_type parse_url_impl(std::string_view user_input, (url_data[input_position] == '\\'))) { input_position++; } - state = ada::state::AUTHORITY; + state = state::AUTHORITY; break; } - case ada::state::QUERY: { + case state::QUERY: { ada_log("QUERY ", helpers::substring(url_data, input_position)); if constexpr (store_values) { // Let queryPercentEncodeSet be the special-query percent-encode set // if url is special; otherwise the query percent-encode set. const uint8_t* query_percent_encode_set = - url.is_special() - ? ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE - : ada::character_sets::QUERY_PERCENT_ENCODE; + url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE + : character_sets::QUERY_PERCENT_ENCODE; // Percent-encode after encoding, with encoding, buffer, and // queryPercentEncodeSet, and append the result to url's query. - url.update_base_search(helpers::substring(url_data, input_position), + url.update_base_search(url_data.substr(input_position), query_percent_encode_set); ada_log("QUERY update_base_search completed "); if (fragment.has_value()) { @@ -12691,11 +13259,10 @@ result_type parse_url_impl(std::string_view user_input, } return url; } - case ada::state::HOST: { + case state::HOST: { ada_log("HOST ", helpers::substring(url_data, input_position)); - std::string_view host_view = - helpers::substring(url_data, input_position); + std::string_view host_view = url_data.substr(input_position); auto [location, found_colon] = helpers::get_host_delimiter_location(url.is_special(), host_view); input_position = (location != std::string_view::npos) @@ -12715,7 +13282,7 @@ result_type parse_url_impl(std::string_view user_input, ada_log("HOST parsing results in ", url.get_hostname()); // Set url's host to host, buffer to the empty string, and state to // port state. - state = ada::state::PORT; + state = state::PORT; input_position++; } // Otherwise, if one of the following is true: @@ -12726,7 +13293,7 @@ result_type parse_url_impl(std::string_view user_input, else { // If url is special and host_view is the empty string, validation // error, return failure. - if (url.is_special() && host_view.empty()) { + if (host_view.empty() && url.is_special()) { url.is_valid = false; return url; } @@ -12742,20 +13309,20 @@ result_type parse_url_impl(std::string_view user_input, " href=", url.get_href()); // Set url's host to host, and state to path start state. - state = ada::state::PATH_START; + state = state::PATH_START; } break; } - case ada::state::OPAQUE_PATH: { + case state::OPAQUE_PATH: { ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position)); - std::string_view view = helpers::substring(url_data, input_position); + std::string_view view = url_data.substr(input_position); // If c is U+003F (?), then set url's query to the empty string and // state to query state. size_t location = view.find('?'); if (location != std::string_view::npos) { view.remove_suffix(view.size() - location); - state = ada::state::QUERY; + state = state::QUERY; input_position += location + 1; } else { input_position = input_size + 1; @@ -12767,25 +13334,23 @@ result_type parse_url_impl(std::string_view user_input, view, character_sets::C0_CONTROL_PERCENT_ENCODE)); break; } - case ada::state::PORT: { + case state::PORT: { ada_log("PORT ", helpers::substring(url_data, input_position)); - std::string_view port_view = - helpers::substring(url_data, input_position); - size_t consumed_bytes = url.parse_port(port_view, true); - input_position += consumed_bytes; + std::string_view port_view = url_data.substr(input_position); + input_position += url.parse_port(port_view, true); if (!url.is_valid) { return url; } state = state::PATH_START; [[fallthrough]]; } - case ada::state::PATH_START: { + case state::PATH_START: { ada_log("PATH_START ", helpers::substring(url_data, input_position)); // If url is special, then: if (url.is_special()) { // Set state to path state. - state = ada::state::PATH; + state = state::PATH; // Optimization: Avoiding going into PATH state improves the // performance of urls ending with /. @@ -12810,12 +13375,12 @@ result_type parse_url_impl(std::string_view user_input, // set url's query to the empty string and state to query state. else if ((input_position != input_size) && (url_data[input_position] == '?')) { - state = ada::state::QUERY; + state = state::QUERY; } // Otherwise, if c is not the EOF code point: else if (input_position != input_size) { // Set state to path state. - state = ada::state::PATH; + state = state::PATH; // If c is not U+002F (/), then decrease pointer by 1. if (url_data[input_position] != '/') { @@ -12826,15 +13391,15 @@ result_type parse_url_impl(std::string_view user_input, input_position++; break; } - case ada::state::PATH: { - std::string_view view = helpers::substring(url_data, input_position); + case state::PATH: { ada_log("PATH ", helpers::substring(url_data, input_position)); + std::string_view view = url_data.substr(input_position); // Most time, we do not need percent encoding. // Furthermore, we can immediately locate the '?'. size_t locofquestionmark = view.find('?'); if (locofquestionmark != std::string_view::npos) { - state = ada::state::QUERY; + state = state::QUERY; view.remove_suffix(view.size() - locofquestionmark); input_position += locofquestionmark + 1; } else { @@ -12850,7 +13415,7 @@ result_type parse_url_impl(std::string_view user_input, } break; } - case ada::state::FILE_SLASH: { + case state::FILE_SLASH: { ada_log("FILE_SLASH ", helpers::substring(url_data, input_position)); // If c is U+002F (/) or U+005C (\), then: @@ -12859,21 +13424,19 @@ result_type parse_url_impl(std::string_view user_input, url_data[input_position] == '\\')) { ada_log("FILE_SLASH c is U+002F or U+005C"); // Set state to file host state. - state = ada::state::FILE_HOST; + state = state::FILE_HOST; input_position++; } else { ada_log("FILE_SLASH otherwise"); // If base is non-null and base's scheme is "file", then: // Note: it is unsafe to do base_url->scheme unless you know that // base_url_has_value() is true. - if (base_url != nullptr && - base_url->type == ada::scheme::type::FILE) { + if (base_url != nullptr && base_url->type == scheme::type::FILE) { // Set url's host to base's host. if constexpr (result_type_is_ada_url) { url.host = base_url->host; } else { - // TODO: Optimization opportunity. - url.set_host(base_url->get_host()); + url.update_host_to_base_host(base_url->get_host()); } // If the code point substring from pointer to the end of input does // not start with a Windows drive letter and base's path[0] is a @@ -12881,7 +13444,7 @@ result_type parse_url_impl(std::string_view user_input, // url's path. if (!base_url->get_pathname().empty()) { if (!checkers::is_windows_drive_letter( - helpers::substring(url_data, input_position))) { + url_data.substr(input_position))) { std::string_view first_base_url_path = base_url->get_pathname().substr(1); size_t loc = first_base_url_path.find('/'); @@ -12903,14 +13466,14 @@ result_type parse_url_impl(std::string_view user_input, } // Set state to path state, and decrease pointer by 1. - state = ada::state::PATH; + state = state::PATH; } break; } - case ada::state::FILE_HOST: { - std::string_view view = helpers::substring(url_data, input_position); + case state::FILE_HOST: { ada_log("FILE_HOST ", helpers::substring(url_data, input_position)); + std::string_view view = url_data.substr(input_position); size_t location = view.find_first_of("/\\?"); std::string_view file_host_buffer( @@ -12918,7 +13481,7 @@ result_type parse_url_impl(std::string_view user_input, (location != std::string_view::npos) ? location : view.size()); if (checkers::is_windows_drive_letter(file_host_buffer)) { - state = ada::state::PATH; + state = state::PATH; } else if (file_host_buffer.empty()) { // Set url's host to the empty string. if constexpr (result_type_is_ada_url) { @@ -12927,7 +13490,7 @@ result_type parse_url_impl(std::string_view user_input, url.update_base_hostname(""); } // Set state to path start state. - state = ada::state::PATH_START; + state = state::PATH_START; } else { size_t consumed_bytes = file_host_buffer.size(); input_position += consumed_bytes; @@ -12949,15 +13512,14 @@ result_type parse_url_impl(std::string_view user_input, } // Set buffer to the empty string and state to path start state. - state = ada::state::PATH_START; + state = state::PATH_START; } break; } - case ada::state::FILE: { + case state::FILE: { ada_log("FILE ", helpers::substring(url_data, input_position)); - std::string_view file_view = - helpers::substring(url_data, input_position); + std::string_view file_view = url_data.substr(input_position); url.set_protocol_as_file(); if constexpr (result_type_is_ada_url) { @@ -12972,11 +13534,10 @@ result_type parse_url_impl(std::string_view user_input, url_data[input_position] == '\\')) { ada_log("FILE c is U+002F or U+005C"); // Set state to file slash state. - state = ada::state::FILE_SLASH; + state = state::FILE_SLASH; } // Otherwise, if base is non-null and base's scheme is "file": - else if (base_url != nullptr && - base_url->type == ada::scheme::type::FILE) { + else if (base_url != nullptr && base_url->type == scheme::type::FILE) { // Set url's host to base's host, url's path to a clone of base's // path, and url's query to base's query. ada_log("FILE base non-null"); @@ -12985,9 +13546,7 @@ result_type parse_url_impl(std::string_view user_input, url.path = base_url->path; url.query = base_url->query; } else { - // TODO: Get rid of set_hostname and replace it with - // update_base_hostname - url.set_hostname(base_url->get_hostname()); + url.update_host_to_base_host(base_url->get_hostname()); url.update_base_pathname(base_url->get_pathname()); url.update_base_search(base_url->get_search()); } @@ -12996,7 +13555,7 @@ result_type parse_url_impl(std::string_view user_input, // If c is U+003F (?), then set url's query to the empty string and // state to query state. if (input_position != input_size && url_data[input_position] == '?') { - state = ada::state::QUERY; + state = state::QUERY; } // Otherwise, if c is not the EOF code point: else if (input_position != input_size) { @@ -13010,7 +13569,7 @@ result_type parse_url_impl(std::string_view user_input, } else { std::string_view path = url.get_pathname(); if (helpers::shorten_path(path, url.type)) { - url.update_base_pathname(std::string(path)); + url.update_base_pathname(std::move(std::string(path))); } } } @@ -13022,14 +13581,14 @@ result_type parse_url_impl(std::string_view user_input, } // Set state to path state and decrease pointer by 1. - state = ada::state::PATH; + state = state::PATH; break; } } // Otherwise, set state to path state, and decrease pointer by 1. else { ada_log("FILE go to path"); - state = ada::state::PATH; + state = state::PATH; break; } @@ -13037,7 +13596,7 @@ result_type parse_url_impl(std::string_view user_input, break; } default: - ada::unreachable(); + unreachable(); } } if constexpr (store_values) { @@ -13067,86 +13626,10 @@ template url_aggregator parse_url( /* end file src/parser.cpp */ /* begin file src/url_components.cpp */ -#include #include namespace ada { -[[nodiscard]] bool url_components::check_offset_consistency() const noexcept { - /** - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - */ - // These conditions can be made more strict. - uint32_t index = 0; - - if (protocol_end == url_components::omitted) { - return false; - } - if (protocol_end < index) { - return false; - } - index = protocol_end; - - if (username_end == url_components::omitted) { - return false; - } - if (username_end < index) { - return false; - } - index = username_end; - - if (host_start == url_components::omitted) { - return false; - } - if (host_start < index) { - return false; - } - index = host_start; - - if (port != url_components::omitted) { - if (port > 0xffff) { - return false; - } - uint32_t port_length = helpers::fast_digit_count(port) + 1; - if (index + port_length < index) { - return false; - } - index += port_length; - } - - if (pathname_start == url_components::omitted) { - return false; - } - if (pathname_start < index) { - return false; - } - index = pathname_start; - - if (search_start != url_components::omitted) { - if (search_start < index) { - return false; - } - index = search_start; - } - - if (hash_start != url_components::omitted) { - if (hash_start < index) { - return false; - } - } - - return true; -} - [[nodiscard]] std::string url_components::to_string() const { std::string answer; auto back = std::back_insert_iterator(answer); @@ -13205,13 +13688,13 @@ template std::string_view input{input_with_colon}; input.remove_suffix(1); auto parsed_type = ada::scheme::get_scheme_type(input); - bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL); + const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL); /** * In the common case, we will immediately recognize a special scheme (e.g., *http, https), in which case, we can go really fast. **/ if (is_input_special) { // fast path!!! - if (has_state_override) { + if constexpr (has_state_override) { // If url's scheme is not a special scheme and buffer is a special scheme, // then return. if (is_special() != is_input_special) { @@ -13236,7 +13719,7 @@ template type = parsed_type; set_scheme_from_view_with_colon(input_with_colon); - if (has_state_override) { + if constexpr (has_state_override) { // This is uncommon. uint16_t urls_scheme_port = get_special_port(); @@ -13253,7 +13736,7 @@ template // need to check the return value. unicode::to_lower_ascii(_buffer.data(), _buffer.size()); - if (has_state_override) { + if constexpr (has_state_override) { // If url's scheme is a special scheme and buffer is not a special scheme, // then return. If url's scheme is not a special scheme and buffer is a // special scheme, then return. @@ -13278,7 +13761,7 @@ template set_scheme(_buffer); - if (has_state_override) { + if constexpr (has_state_override) { // This is uncommon. uint16_t urls_scheme_port = get_special_port(); @@ -13407,7 +13890,7 @@ bool url_aggregator::set_protocol(const std::string_view input) { view.append(":"); std::string::iterator pointer = - std::find_if_not(view.begin(), view.end(), unicode::is_alnum_plus); + std::ranges::find_if_not(view, unicode::is_alnum_plus); if (pointer != view.end() && *pointer == ':') { return parse_scheme_with_colon( @@ -13469,12 +13952,9 @@ bool url_aggregator::set_port(const std::string_view input) { clear_port(); return true; } - // Input should not start with control characters. - if (ada::unicode::is_c0_control_or_space(trimmed.front())) { - return false; - } - // Input should contain at least one ascii digit. - if (input.find_first_of("0123456789") == std::string_view::npos) { + + // Input should not start with a non-digit character. + if (!ada::unicode::is_ascii_digit(trimmed.front())) { return false; } @@ -13499,8 +13979,7 @@ bool url_aggregator::set_pathname(const std::string_view input) { } clear_pathname(); parse_path(input); - if (checkers::begins_with(get_pathname(), "//") && !has_authority() && - !has_dash_dot()) { + if (get_pathname().starts_with("//") && !has_authority() && !has_dash_dot()) { buffer.insert(components.pathname_start, "/."); components.pathname_start += 2; } @@ -13724,7 +14203,7 @@ bool url_aggregator::set_host_or_hostname(const std::string_view input) { // Note: the 'found_colon' value is true if and only if a colon was // encountered while not inside brackets. if (found_colon) { - if (override_hostname) { + if constexpr (override_hostname) { return false; } std::string_view sub_buffer = new_host.substr(location + 1); @@ -13908,21 +14387,6 @@ bool url_aggregator::set_hostname(const std::string_view input) { return helpers::substring(buffer, start, components.host_end); } -[[nodiscard]] std::string_view url_aggregator::get_pathname() const noexcept - ada_lifetime_bound { - ada_log("url_aggregator::get_pathname pathname_start = ", - components.pathname_start, " buffer.size() = ", buffer.size(), - " components.search_start = ", components.search_start, - " components.hash_start = ", components.hash_start); - auto ending_index = uint32_t(buffer.size()); - if (components.search_start != url_components::omitted) { - ending_index = components.search_start; - } else if (components.hash_start != url_components::omitted) { - ending_index = components.hash_start; - } - return helpers::substring(buffer, components.pathname_start, ending_index); -} - [[nodiscard]] std::string_view url_aggregator::get_search() const noexcept ada_lifetime_bound { ada_log("url_aggregator::get_search"); @@ -14562,245 +15026,75 @@ bool url_aggregator::parse_opaque_host(std::string_view input) { return answer; } -[[nodiscard]] bool url_aggregator::validate() const noexcept { - if (!is_valid) { - return true; +void url_aggregator::delete_dash_dot() { + ada_log("url_aggregator::delete_dash_dot"); + ADA_ASSERT_TRUE(validate()); + ADA_ASSERT_TRUE(has_dash_dot()); + buffer.erase(components.host_end, 2); + components.pathname_start -= 2; + if (components.search_start != url_components::omitted) { + components.search_start -= 2; } - if (!components.check_offset_consistency()) { - ada_log("url_aggregator::validate inconsistent components \n", - to_diagram()); - return false; + if (components.hash_start != url_components::omitted) { + components.hash_start -= 2; } - // We have a credible components struct, but let us investivate more - // carefully: - /** - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end + ADA_ASSERT_TRUE(validate()); + ADA_ASSERT_TRUE(!has_dash_dot()); +} + +inline void url_aggregator::consume_prepared_path(std::string_view input) { + ada_log("url_aggregator::consume_prepared_path ", input); + /*** + * This is largely duplicated code from helpers::parse_prepared_path, which is + * unfortunate. This particular function is nearly identical, except that it + * is a method on url_aggregator. The idea is that the trivial path (which is + * very common) merely appends to the buffer. This is the same trivial path as + * with helpers::parse_prepared_path, except that we have the additional check + * for is_at_path(). Otherwise, we grab a copy of the current path and we + * modify it, and then insert it back into the buffer. */ - if (components.protocol_end == url_components::omitted) { - ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram()); - return false; - } - if (components.username_end == url_components::omitted) { - ada_log("url_aggregator::validate omitted username_end \n", to_diagram()); - return false; - } - if (components.host_start == url_components::omitted) { - ada_log("url_aggregator::validate omitted host_start \n", to_diagram()); - return false; - } - if (components.host_end == url_components::omitted) { - ada_log("url_aggregator::validate omitted host_end \n", to_diagram()); - return false; + uint8_t accumulator = checkers::path_signature(input); + // Let us first detect a trivial case. + // If it is special, we check that we have no dot, no %, no \ and no + // character needing percent encoding. Otherwise, we check that we have no %, + // no dot, and no character needing percent encoding. + constexpr uint8_t need_encoding = 1; + constexpr uint8_t backslash_char = 2; + constexpr uint8_t dot_char = 4; + constexpr uint8_t percent_char = 8; + bool special = type != ada::scheme::NOT_SPECIAL; + bool may_need_slow_file_handling = (type == ada::scheme::type::FILE && + checkers::is_windows_drive_letter(input)); + bool trivial_path = + (special ? (accumulator == 0) + : ((accumulator & (need_encoding | dot_char | percent_char)) == + 0)) && + (!may_need_slow_file_handling); + if (accumulator == dot_char && !may_need_slow_file_handling) { + // '4' means that we have at least one dot, but nothing that requires + // percent encoding or decoding. The only part that is not trivial is + // that we may have single dots and double dots path segments. + // If we have such segments, then we either have a path that begins + // with '.' (easy to check), or we have the sequence './'. + // Note: input cannot be empty, it must at least contain one character ('.') + // Note: we know that '\' is not present. + if (input[0] != '.') { + size_t slashdot = input.find("/."); + if (slashdot == std::string_view::npos) { // common case + trivial_path = true; + } else { // uncommon + // only three cases matter: /./, /.. or a final / + trivial_path = + !(slashdot + 2 == input.size() || input[slashdot + 2] == '.' || + input[slashdot + 2] == '/'); + } + } } - if (components.pathname_start == url_components::omitted) { - ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram()); - return false; - } - - if (components.protocol_end > buffer.size()) { - ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram()); - return false; - } - if (components.username_end > buffer.size()) { - ada_log("url_aggregator::validate username_end overflow \n", to_diagram()); - return false; - } - if (components.host_start > buffer.size()) { - ada_log("url_aggregator::validate host_start overflow \n", to_diagram()); - return false; - } - if (components.host_end > buffer.size()) { - ada_log("url_aggregator::validate host_end overflow \n", to_diagram()); - return false; - } - if (components.pathname_start > buffer.size()) { - ada_log("url_aggregator::validate pathname_start overflow \n", - to_diagram()); - return false; - } - - if (components.protocol_end > 0) { - if (buffer[components.protocol_end - 1] != ':') { - ada_log( - "url_aggregator::validate missing : at the end of the protocol \n", - to_diagram()); - return false; - } - } - - if (components.username_end != buffer.size() && - components.username_end > components.protocol_end + 2) { - if (buffer[components.username_end] != ':' && - buffer[components.username_end] != '@') { - ada_log( - "url_aggregator::validate missing : or @ at the end of the username " - "\n", - to_diagram()); - return false; - } - } - - if (components.host_start != buffer.size()) { - if (components.host_start > components.username_end) { - if (buffer[components.host_start] != '@') { - ada_log( - "url_aggregator::validate missing @ at the end of the password \n", - to_diagram()); - return false; - } - } else if (components.host_start == components.username_end && - components.host_end > components.host_start) { - if (components.host_start == components.protocol_end + 2) { - if (buffer[components.protocol_end] != '/' || - buffer[components.protocol_end + 1] != '/') { - ada_log( - "url_aggregator::validate missing // between protocol and host " - "\n", - to_diagram()); - return false; - } - } else { - if (components.host_start > components.protocol_end && - buffer[components.host_start] != '@') { - ada_log( - "url_aggregator::validate missing @ at the end of the username " - "\n", - to_diagram()); - return false; - } - } - } else { - if (components.host_end != components.host_start) { - ada_log("url_aggregator::validate expected omitted host \n", - to_diagram()); - return false; - } - } - } - if (components.host_end != buffer.size() && - components.pathname_start > components.host_end) { - if (components.pathname_start == components.host_end + 2 && - buffer[components.host_end] == '/' && - buffer[components.host_end + 1] == '.') { - if (components.pathname_start + 1 >= buffer.size() || - buffer[components.pathname_start] != '/' || - buffer[components.pathname_start + 1] != '/') { - ada_log( - "url_aggregator::validate expected the path to begin with // \n", - to_diagram()); - return false; - } - } else if (buffer[components.host_end] != ':') { - ada_log("url_aggregator::validate missing : at the port \n", - to_diagram()); - return false; - } - } - if (components.pathname_start != buffer.size() && - components.pathname_start < components.search_start && - components.pathname_start < components.hash_start && !has_opaque_path) { - if (buffer[components.pathname_start] != '/') { - ada_log("url_aggregator::validate missing / at the path \n", - to_diagram()); - return false; - } - } - if (components.search_start != url_components::omitted) { - if (buffer[components.search_start] != '?') { - ada_log("url_aggregator::validate missing ? at the search \n", - to_diagram()); - return false; - } - } - if (components.hash_start != url_components::omitted) { - if (buffer[components.hash_start] != '#') { - ada_log("url_aggregator::validate missing # at the hash \n", - to_diagram()); - return false; - } - } - - return true; -} - -void url_aggregator::delete_dash_dot() { - ada_log("url_aggregator::delete_dash_dot"); - ADA_ASSERT_TRUE(validate()); - ADA_ASSERT_TRUE(has_dash_dot()); - buffer.erase(components.host_end, 2); - components.pathname_start -= 2; - if (components.search_start != url_components::omitted) { - components.search_start -= 2; - } - if (components.hash_start != url_components::omitted) { - components.hash_start -= 2; - } - ADA_ASSERT_TRUE(validate()); - ADA_ASSERT_TRUE(!has_dash_dot()); -} - -inline void url_aggregator::consume_prepared_path(std::string_view input) { - ada_log("url_aggregator::consume_prepared_path ", input); - /*** - * This is largely duplicated code from helpers::parse_prepared_path, which is - * unfortunate. This particular function is nearly identical, except that it - * is a method on url_aggregator. The idea is that the trivial path (which is - * very common) merely appends to the buffer. This is the same trivial path as - * with helpers::parse_prepared_path, except that we have the additional check - * for is_at_path(). Otherwise, we grab a copy of the current path and we - * modify it, and then insert it back into the buffer. - */ - uint8_t accumulator = checkers::path_signature(input); - // Let us first detect a trivial case. - // If it is special, we check that we have no dot, no %, no \ and no - // character needing percent encoding. Otherwise, we check that we have no %, - // no dot, and no character needing percent encoding. - constexpr uint8_t need_encoding = 1; - constexpr uint8_t backslash_char = 2; - constexpr uint8_t dot_char = 4; - constexpr uint8_t percent_char = 8; - bool special = type != ada::scheme::NOT_SPECIAL; - bool may_need_slow_file_handling = (type == ada::scheme::type::FILE && - checkers::is_windows_drive_letter(input)); - bool trivial_path = - (special ? (accumulator == 0) - : ((accumulator & (need_encoding | dot_char | percent_char)) == - 0)) && - (!may_need_slow_file_handling); - if (accumulator == dot_char && !may_need_slow_file_handling) { - // '4' means that we have at least one dot, but nothing that requires - // percent encoding or decoding. The only part that is not trivial is - // that we may have single dots and double dots path segments. - // If we have such segments, then we either have a path that begins - // with '.' (easy to check), or we have the sequence './'. - // Note: input cannot be empty, it must at least contain one character ('.') - // Note: we know that '\' is not present. - if (input[0] != '.') { - size_t slashdot = input.find("/."); - if (slashdot == std::string_view::npos) { // common case - trivial_path = true; - } else { // uncommon - // only three cases matter: /./, /.. or a final / - trivial_path = - !(slashdot + 2 == input.size() || input[slashdot + 2] == '.' || - input[slashdot + 2] == '/'); - } - } - } - if (trivial_path && is_at_path()) { - ada_log("parse_path trivial"); - buffer += '/'; - buffer += input; - return; + if (trivial_path && is_at_path()) { + ada_log("parse_path trivial"); + buffer += '/'; + buffer += input; + return; } std::string path = std::string(get_pathname()); // We are going to need to look a bit at the path, but let us see if we can @@ -14922,6 +15216,1528 @@ inline void url_aggregator::consume_prepared_path(std::string_view input) { } } // namespace ada /* end file src/url_aggregator.cpp */ +/* begin file src/url_pattern.cpp */ + +#include +#include +#include + +namespace ada { +// The default options is an options struct with delimiter code point set to +// the empty string and prefix code point set to the empty string. +url_pattern_compile_component_options + url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt); + +// The hostname options is an options struct with delimiter code point set +// "." and prefix code point set to the empty string. +url_pattern_compile_component_options + url_pattern_compile_component_options::HOSTNAME('.', std::nullopt); + +// The pathname options is an options struct with delimiter code point set +// "/" and prefix code point set to "/". +url_pattern_compile_component_options + url_pattern_compile_component_options::PATHNAME('/', '/'); + +tl::expected url_pattern_init::process( + url_pattern_init init, std::string_view type, + std::optional protocol, + std::optional username, + std::optional password, + std::optional hostname, + std::optional port, + std::optional pathname, + std::optional search, + std::optional hash) { + // Let result be the result of creating a new URLPatternInit. + auto result = url_pattern_init{}; + + // If protocol is not null, set result["protocol"] to protocol. + if (protocol.has_value()) result.protocol = *protocol; + + // If username is not null, set result["username"] to username. + if (username.has_value()) result.username = *username; + + // If password is not null, set result["password"] to password. + if (password.has_value()) result.password = *password; + + // If hostname is not null, set result["hostname"] to hostname. + if (hostname.has_value()) result.hostname = *hostname; + + // If port is not null, set result["port"] to port. + if (port.has_value()) result.port = *port; + + // If pathname is not null, set result["pathname"] to pathname. + if (pathname.has_value()) result.pathname = *pathname; + + // If search is not null, set result["search"] to search. + if (search.has_value()) result.search = *search; + + // If hash is not null, set result["hash"] to hash. + if (hash.has_value()) result.hash = *hash; + + // Let baseURL be null. + std::optional base_url{}; + + // If init["baseURL"] exists: + if (init.base_url.has_value()) { + // Set baseURL to the result of parsing init["baseURL"]. + auto parsing_result = ada::parse(*init.base_url); + // If baseURL is failure, then throw a TypeError. + if (!parsing_result) { + return tl::unexpected(errors::type_error); + } + base_url = std::move(*parsing_result); + + // If init["protocol"] does not exist, then set result["protocol"] to the + // result of processing a base URL string given baseURL’s scheme and type. + if (!init.protocol.has_value()) { + ADA_ASSERT_TRUE(base_url.has_value()); + std::string_view base_url_protocol = base_url->get_protocol(); + if (base_url_protocol.ends_with(":")) base_url_protocol.remove_suffix(1); + result.protocol = + url_pattern_helpers::process_base_url_string(base_url_protocol, type); + } + + // If type is not "pattern" and init contains none of "protocol", + // "hostname", "port" and "username", then set result["username"] to the + // result of processing a base URL string given baseURL’s username and type. + if (type != "pattern" && !init.protocol && !init.hostname && !init.port && + !init.username) { + result.username = url_pattern_helpers::process_base_url_string( + base_url->get_username(), type); + } + + // TODO: Optimization opportunity: Merge this with the previous check. + // If type is not "pattern" and init contains none of "protocol", + // "hostname", "port", "username" and "password", then set + // result["password"] to the result of processing a base URL string given + // baseURL’s password and type. + if (type != "pattern" && !init.protocol && !init.hostname && !init.port && + !init.username && !init.password) { + result.password = url_pattern_helpers::process_base_url_string( + base_url->get_password(), type); + } + + // If init contains neither "protocol" nor "hostname", then: + if (!init.protocol && !init.hostname) { + // Let baseHost be baseURL’s host. + // If baseHost is null, then set baseHost to the empty string. + auto base_host = base_url->get_hostname(); + // Set result["hostname"] to the result of processing a base URL string + // given baseHost and type. + result.hostname = + url_pattern_helpers::process_base_url_string(base_host, type); + } + + // If init contains none of "protocol", "hostname", and "port", then: + if (!init.protocol && !init.hostname && !init.port) { + // If baseURL’s port is null, then set result["port"] to the empty string. + // Otherwise, set result["port"] to baseURL’s port, serialized. + result.port = base_url->get_port(); + } + + // If init contains none of "protocol", "hostname", "port", and "pathname", + // then set result["pathname"] to the result of processing a base URL string + // given the result of URL path serializing baseURL and type. + if (!init.protocol && !init.hostname && !init.port && !init.pathname) { + result.pathname = url_pattern_helpers::process_base_url_string( + base_url->get_pathname(), type); + } + + // If init contains none of "protocol", "hostname", "port", "pathname", and + // "search", then: + if (!init.protocol && !init.hostname && !init.port && !init.pathname && + !init.search) { + // Let baseQuery be baseURL’s query. + // Set result["search"] to the result of processing a base URL string + // given baseQuery and type. + result.search = url_pattern_helpers::process_base_url_string( + base_url->get_search(), type); + } + + // If init contains none of "protocol", "hostname", "port", "pathname", + // "search", and "hash", then: + if (!init.protocol && !init.hostname && !init.port && !init.pathname && + !init.search && !init.hash) { + // Let baseFragment be baseURL’s fragment. + // Set result["hash"] to the result of processing a base URL string given + // baseFragment and type. + result.hash = url_pattern_helpers::process_base_url_string( + base_url->get_hash(), type); + } + } + + // If init["protocol"] exists, then set result["protocol"] to the result of + // process protocol for init given init["protocol"] and type. + if (init.protocol) { + auto process_result = process_protocol(*init.protocol, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.protocol = std::move(*process_result); + } + + // If init["username"] exists, then set result["username"] to the result of + // process username for init given init["username"] and type. + if (init.username.has_value()) { + auto process_result = process_username(*init.username, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.username = std::move(*process_result); + } + + // If init["password"] exists, then set result["password"] to the result of + // process password for init given init["password"] and type. + if (init.password.has_value()) { + auto process_result = process_password(*init.password, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.password = std::move(*process_result); + } + + // If init["hostname"] exists, then set result["hostname"] to the result of + // process hostname for init given init["hostname"] and type. + if (init.hostname.has_value()) { + auto process_result = process_hostname(*init.hostname, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.hostname = std::move(*process_result); + } + + // If init["port"] exists, then set result["port"] to the result of process + // port for init given init["port"], result["protocol"], and type. + if (init.port) { + auto process_result = + process_port(*init.port, result.protocol.value_or("fake"), type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.port = std::move(*process_result); + } + + // If init["pathname"] exists: + if (init.pathname.has_value()) { + // Set result["pathname"] to init["pathname"]. + result.pathname = init.pathname; + + // If the following are all true: + // - baseURL is not null; + // - baseURL has an opaque path; and + // - the result of running is an absolute pathname given result["pathname"] + // and type is false, + if (base_url && !base_url->has_opaque_path && + !url_pattern_helpers::is_absolute_pathname(*result.pathname, type)) { + // Let baseURLPath be the result of running process a base URL string + // given the result of URL path serializing baseURL and type. + std::string base_url_path = url_pattern_helpers::process_base_url_string( + base_url->get_pathname(), type); + + // Let slash index be the index of the last U+002F (/) code point found in + // baseURLPath, interpreted as a sequence of code points, or null if there + // are no instances of the code point. + auto slash_index = base_url_path.find_last_of('/'); + + // If slash index is not null: + if (slash_index != std::string::npos) { + // Let new pathname be the code point substring from 0 to slash index + + // 1 within baseURLPath. + std::string new_pathname = base_url_path.substr(0, slash_index + 1); + // Append result["pathname"] to the end of new pathname. + ADA_ASSERT_TRUE(result.pathname.has_value()); + new_pathname.append(result.pathname.value()); + // Set result["pathname"] to new pathname. + result.pathname = std::move(new_pathname); + } + } + + // Set result["pathname"] to the result of process pathname for init given + // result["pathname"], result["protocol"], and type. + auto pathname_processing_result = + process_pathname(*result.pathname, result.protocol.value_or(""), type); + if (!pathname_processing_result) { + return tl::unexpected(pathname_processing_result.error()); + } + result.pathname = std::move(*pathname_processing_result); + } + + // If init["search"] exists then set result["search"] to the result of process + // search for init given init["search"] and type. + if (init.search) { + auto process_result = process_search(*init.search, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.search = std::move(*process_result); + } + + // If init["hash"] exists then set result["hash"] to the result of process + // hash for init given init["hash"] and type. + if (init.hash) { + auto process_result = process_hash(*init.hash, type); + if (!process_result) { + return tl::unexpected(process_result.error()); + } + result.hash = std::move(*process_result); + } + // Return result. + return result; +} + +tl::expected url_pattern_init::process_protocol( + std::string_view value, std::string_view type) { + ada_log("process_protocol=", value, " [", type, "]"); + // Let strippedValue be the given value with a single trailing U+003A (:) + // removed, if any. + if (value.ends_with(":")) { + value.remove_suffix(1); + } + // If type is "pattern" then return strippedValue. + if (type == "pattern") { + return std::string(value); + } + // Return the result of running canonicalize a protocol given strippedValue. + return url_pattern_helpers::canonicalize_protocol(value); +} + +tl::expected url_pattern_init::process_username( + std::string_view value, std::string_view type) { + // If type is "pattern" then return value. + if (type == "pattern") { + return std::string(value); + } + // Return the result of running canonicalize a username given value. + return url_pattern_helpers::canonicalize_username(value); +} + +tl::expected url_pattern_init::process_password( + std::string_view value, std::string_view type) { + // If type is "pattern" then return value. + if (type == "pattern") { + return std::string(value); + } + // Return the result of running canonicalize a password given value. + return url_pattern_helpers::canonicalize_password(value); +} + +tl::expected url_pattern_init::process_hostname( + std::string_view value, std::string_view type) { + ada_log("process_hostname value=", value, " type=", type); + // If type is "pattern" then return value. + if (type == "pattern") { + return std::string(value); + } + // Return the result of running canonicalize a hostname given value. + return url_pattern_helpers::canonicalize_hostname(value); +} + +tl::expected url_pattern_init::process_port( + std::string_view port, std::string_view protocol, std::string_view type) { + // If type is "pattern" then return portValue. + if (type == "pattern") { + return std::string(port); + } + // Return the result of running canonicalize a port given portValue and + // protocolValue. + return url_pattern_helpers::canonicalize_port_with_protocol(port, protocol); +} + +tl::expected url_pattern_init::process_pathname( + std::string_view value, std::string_view protocol, std::string_view type) { + // If type is "pattern" then return pathnameValue. + if (type == "pattern") { + return std::string(value); + } + + // If protocolValue is a special scheme or the empty string, then return the + // result of running canonicalize a pathname given pathnameValue. + if (protocol.empty() || scheme::is_special(protocol)) { + return url_pattern_helpers::canonicalize_pathname(value); + } + + // Return the result of running canonicalize an opaque pathname given + // pathnameValue. + return url_pattern_helpers::canonicalize_opaque_pathname(value); +} + +tl::expected url_pattern_init::process_search( + std::string_view value, std::string_view type) { + // Let strippedValue be the given value with a single leading U+003F (?) + // removed, if any. + if (value.starts_with("?")) { + value.remove_prefix(1); + } + ADA_ASSERT_TRUE(!value.starts_with("?")); + // If type is "pattern" then return strippedValue. + if (type == "pattern") { + return std::string(value); + } + // Return the result of running canonicalize a search given strippedValue. + return url_pattern_helpers::canonicalize_search(value); +} + +tl::expected url_pattern_init::process_hash( + std::string_view value, std::string_view type) { + // Let strippedValue be the given value with a single leading U+0023 (#) + // removed, if any. + if (value.starts_with("#")) { + value.remove_prefix(1); + } + ADA_ASSERT_TRUE(!value.starts_with("#")); + // If type is "pattern" then return strippedValue. + if (type == "pattern") { + return std::string(value); + } + // Return the result of running canonicalize a hash given strippedValue. + return url_pattern_helpers::canonicalize_hash(value); +} + +std::string url_pattern_options::to_string() const { + std::string answer; + answer.append("{\n"); + answer.append("\t\"ignore_case\":\""); + answer.append(ignore_case ? "true" : "false"); + answer.append("\",\n"); + answer.append("}"); + return answer; +} + +std::string url_pattern_init::to_string() const { + std::string answer; + auto back = std::back_insert_iterator(answer); + answer.append("{\n"); + + if (protocol.has_value()) { + answer.append("\t\"protocol\":\""); + helpers::encode_json(protocol.value(), back); + answer.append("\",\n"); + } + + if (username.has_value()) { + answer.append("\t\"username\":\""); + helpers::encode_json(username.value(), back); + answer.append("\",\n"); + } + + if (password.has_value()) { + answer.append("\t\"password\":\""); + helpers::encode_json(password.value(), back); + answer.append("\",\n"); + } + + if (hostname.has_value()) { + answer.append("\t\"hostname\":\""); + helpers::encode_json(hostname.value(), back); + answer.append("\",\n"); + } + + if (port.has_value()) { + answer.append("\t\"port\":\""); + helpers::encode_json(port.value(), back); + answer.append("\",\n"); + } + + if (pathname.has_value()) { + answer.append("\t\"pathname\":\""); + helpers::encode_json(pathname.value(), back); + answer.append("\",\n"); + } + + if (search.has_value()) { + answer.append("\t\"search\":\""); + helpers::encode_json(search.value(), back); + answer.append("\",\n"); + } + + if (hash.has_value()) { + answer.append("\t\"hash\":\""); + helpers::encode_json(hash.value(), back); + answer.append("\",\n"); + } + + if (base_url.has_value()) { + answer.append("\t\"base_url\":\""); + helpers::encode_json(base_url.value(), back); + answer.append("\",\n"); + } + + answer.append("}"); + return answer; +} + +} // namespace ada +/* end file src/url_pattern.cpp */ +/* begin file src/url_pattern_helpers.cpp */ + +#include +#include +#include + +namespace ada::url_pattern_helpers { + +std::tuple> +generate_regular_expression_and_name_list( + std::vector& part_list, + url_pattern_compile_component_options options) { + // Let result be "^" + std::string result = "^"; + + // Let name list be a new list + std::vector name_list{}; + const std::string full_wildcard_regexp_value = ".*"; + + // For each part of part list: + for (const url_pattern_part& part : part_list) { + // If part's type is "fixed-text": + if (part.type == url_pattern_part_type::FIXED_TEXT) { + // If part's modifier is "none" + if (part.modifier == url_pattern_part_modifier::NONE) { + // Append the result of running escape a regexp string given part's + // value + result += escape_regexp_string(part.value); + } else { + // A "fixed-text" part with a modifier uses a non capturing group + // (?:) + // Append "(?:" to the end of result. + result.append("(?:"); + // Append the result of running escape a regexp string given part’s + // value to the end of result. + result.append(escape_regexp_string(part.value)); + // Append ")" to the end of result. + result.append(")"); + // Append the result of running convert a modifier to a string given + // part’s modifier to the end of result. + result.append(convert_modifier_to_string(part.modifier)); + } + continue; + } + + // Assert: part's name is not the empty string + ADA_ASSERT_TRUE(!part.name.empty()); + + // Append part's name to name list + name_list.push_back(part.name); + + // Let regexp value be part's value + std::string regexp_value = part.value; + + // If part's type is "segment-wildcard" + if (part.type == url_pattern_part_type::SEGMENT_WILDCARD) { + // then set regexp value to the result of running generate a segment + // wildcard regexp given options. + regexp_value = generate_segment_wildcard_regexp(options); + } + // Otherwise if part's type is "full-wildcard" + else if (part.type == url_pattern_part_type::FULL_WILDCARD) { + // then set regexp value to full wildcard regexp value. + regexp_value = full_wildcard_regexp_value; + } + + // If part's prefix is the empty string and part's suffix is the empty + // string + if (part.prefix.empty() && part.suffix.empty()) { + // If part's modifier is "none" or "optional" + if (part.modifier == url_pattern_part_modifier::NONE || + part.modifier == url_pattern_part_modifier::OPTIONAL) { + // () + result += "(" + regexp_value + ")" + + convert_modifier_to_string(part.modifier); + } else { + // ((?:)) + result += "((?:" + regexp_value + ")" + + convert_modifier_to_string(part.modifier) + ")"; + } + continue; + } + + // If part's modifier is "none" or "optional" + if (part.modifier == url_pattern_part_modifier::NONE || + part.modifier == url_pattern_part_modifier::OPTIONAL) { + // (?:()) + result += "(?:" + escape_regexp_string(part.prefix) + "(" + regexp_value + + ")" + escape_regexp_string(part.suffix) + ")" + + convert_modifier_to_string(part.modifier); + continue; + } + + // Assert: part's modifier is "zero-or-more" or "one-or-more" + ADA_ASSERT_TRUE(part.modifier == url_pattern_part_modifier::ZERO_OR_MORE || + part.modifier == url_pattern_part_modifier::ONE_OR_MORE); + + // Assert: part's prefix is not the empty string or part's suffix is not the + // empty string + ADA_ASSERT_TRUE(!part.prefix.empty() || !part.suffix.empty()); + + // (?:((?:)(?:(?:))*))? + // Append "(?:" to the end of result. + result.append("(?:"); + // Append the result of running escape a regexp string given part’s prefix + // to the end of result. + result.append(escape_regexp_string(part.prefix)); + // Append "((?:" to the end of result. + result.append("((?:"); + // Append regexp value to the end of result. + result.append(regexp_value); + // Append ")(?:" to the end of result. + result.append(")(?:"); + // Append the result of running escape a regexp string given part’s suffix + // to the end of result. + result.append(escape_regexp_string(part.suffix)); + // Append the result of running escape a regexp string given part’s prefix + // to the end of result. + result.append(escape_regexp_string(part.prefix)); + // Append "(?:" to the end of result. + result.append("(?:"); + // Append regexp value to the end of result. + result.append(regexp_value); + // Append "))*)" to the end of result. + result.append("))*)"); + // Append the result of running escape a regexp string given part’s suffix + // to the end of result. + result.append(escape_regexp_string(part.suffix)); + // Append ")" to the end of result. + result.append(")"); + + // If part's modifier is "zero-or-more" then append "?" to the end of result + if (part.modifier == url_pattern_part_modifier::ZERO_OR_MORE) { + result += "?"; + } + } + + // Append "$" to the end of result + result += "$"; + + // Return (result, name list) + return {result, name_list}; +} + +bool is_ipv6_address(std::string_view input) noexcept { + // If input’s code point length is less than 2, then return false. + if (input.size() < 2) return false; + + // Let input code points be input interpreted as a list of code points. + // If input code points[0] is U+005B ([), then return true. + if (input.front() == '[') return true; + // If input code points[0] is U+007B ({) and input code points[1] is U+005B + // ([), then return true. + if (input.starts_with("{[")) return true; + // If input code points[0] is U+005C (\) and input code points[1] is U+005B + // ([), then return true. + return input.starts_with("\\["); +} + +std::string convert_modifier_to_string(url_pattern_part_modifier modifier) { + // TODO: Optimize this. + switch (modifier) { + // If modifier is "zero-or-more", then return "*". + case url_pattern_part_modifier::ZERO_OR_MORE: + return "*"; + // If modifier is "optional", then return "?". + case url_pattern_part_modifier::OPTIONAL: + return "?"; + // If modifier is "one-or-more", then return "+". + case url_pattern_part_modifier::ONE_OR_MORE: + return "+"; + // Return the empty string. + default: + return ""; + } +} + +std::string generate_segment_wildcard_regexp( + url_pattern_compile_component_options options) { + // Let result be "[^". + std::string result = "[^"; + // Append the result of running escape a regexp string given options’s + // delimiter code point to the end of result. + result.append(escape_regexp_string(options.get_delimiter())); + // Append "]+?" to the end of result. + result.append("]+?"); + // Return result. + ada_log("generate_segment_wildcard_regexp result: ", result); + return result; +} + +tl::expected canonicalize_protocol( + std::string_view input) { + ada_log("canonicalize_protocol called with input=", input); + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + + // IMPORTANT: Deviation from the spec. We remove the trailing ':' here. + if (input.ends_with(":")) { + input.remove_suffix(1); + } + + // Let dummyURL be a new URL record. + // Let parseResult be the result of running the basic URL parser given value + // followed by "://dummy.test", with dummyURL as url. + if (auto dummy_url = ada::parse( + std::string(input) + "://dummy.test", nullptr)) { + // IMPORTANT: Deviation from the spec. We remove the trailing ':' here. + // Since URL parser always return protocols ending with `:` + auto protocol = dummy_url->get_protocol(); + protocol.remove_suffix(1); + return std::string(protocol); + } + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_username( + std::string_view input) { + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + auto url = ada::parse("fake://dummy.test", nullptr); + ADA_ASSERT_TRUE(url.has_value()); + // Set the username given dummyURL and value. + if (!url->set_username(input)) { + return tl::unexpected(errors::type_error); + } + // Return dummyURL’s username. + return std::string(url->get_username()); +} + +tl::expected canonicalize_password( + std::string_view input) { + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // Set the password given dummyURL and value. + auto url = ada::parse("fake://dummy.test", nullptr); + + ADA_ASSERT_TRUE(url.has_value()); + if (!url->set_password(input)) { + return tl::unexpected(errors::type_error); + } + // Return dummyURL’s password. + return std::string(url->get_password()); +} + +tl::expected canonicalize_hostname( + std::string_view input) { + ada_log("canonicalize_hostname input=", input); + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // Let parseResult be the result of running the basic URL parser given value + // with dummyURL as url and hostname state as state override. + + // IMPORTANT: The protocol needs to be a special protocol, otherwise the + // hostname will not be converted using IDNA. + auto url = ada::parse("https://dummy.test", nullptr); + ADA_ASSERT_TRUE(url); + // if (!isValidHostnameInput(hostname)) return kj::none; + if (!url->set_hostname(input)) { + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); + } + // Return dummyURL’s host, serialized, or empty string if it is null. + return std::string(url->get_hostname()); +} + +tl::expected canonicalize_ipv6_hostname( + std::string_view input) { + ada_log("canonicalize_ipv6_hostname input=", input); + // TODO: Optimization opportunity: Use lookup table to speed up checking + if (std::ranges::any_of(input, [](char c) { + return c != '[' && c != ']' && c != ':' && + !unicode::is_ascii_hex_digit(c); + })) { + return tl::unexpected(errors::type_error); + } + // Append the result of running ASCII lowercase given code point to the end of + // result. + auto hostname = std::string(input); + unicode::to_lower_ascii(hostname.data(), hostname.size()); + return hostname; +} + +tl::expected canonicalize_port( + std::string_view port_value) { + // If portValue is the empty string, return portValue. + if (port_value.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // If protocolValue was given, then set dummyURL’s scheme to protocolValue. + // Let parseResult be the result of running basic URL parser given portValue + // with dummyURL as url and port state as state override. + auto url = ada::parse("fake://dummy.test", nullptr); + ADA_ASSERT_TRUE(url); + if (url->set_port(port_value)) { + // Return dummyURL’s port, serialized, or empty string if it is null. + return std::string(url->get_port()); + } + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_port_with_protocol( + std::string_view port_value, std::string_view protocol) { + // If portValue is the empty string, return portValue. + if (port_value.empty()) [[unlikely]] { + return ""; + } + + // TODO: Remove this + // We have an empty protocol because get_protocol() returns an empty string + // We should handle this in the caller rather than here. + if (protocol.empty()) { + protocol = "fake"; + } else if (protocol.ends_with(":")) { + protocol.remove_suffix(1); + } + // Let dummyURL be a new URL record. + // If protocolValue was given, then set dummyURL’s scheme to protocolValue. + // Let parseResult be the result of running basic URL parser given portValue + // with dummyURL as url and port state as state override. + auto url = ada::parse(std::string(protocol) + "://dummy.test", + nullptr); + // TODO: Remove has_port() check. + // This is actually a bug with url parser where set_port() returns true for + // "invalid80" port value. + if (url && url->set_port(port_value) && url->has_port()) { + // Return dummyURL’s port, serialized, or empty string if it is null. + return std::string(url->get_port()); + } + // TODO: Remove this once the previous has_port() check is removed. + if (url) { + if (scheme::is_special(protocol) && url->get_port().empty()) { + return ""; + } + } + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_pathname( + std::string_view input) { + // If value is the empty string, then return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let leading slash be true if the first code point in value is U+002F (/) + // and otherwise false. + const bool leading_slash = input.starts_with("/"); + // Let modified value be "/-" if leading slash is false and otherwise the + // empty string. + const auto modified_value = leading_slash ? "" : "/-"; + const auto full_url = + std::string("fake://fake-url") + modified_value + std::string(input); + if (auto url = ada::parse(full_url, nullptr)) { + const auto pathname = url->get_pathname(); + // If leading slash is false, then set result to the code point substring + // from 2 to the end of the string within result. + return leading_slash ? std::string(pathname) + : std::string(pathname.substr(2)); + } + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_opaque_pathname( + std::string_view input) { + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // Set dummyURL’s path to the empty string. + // Let parseResult be the result of running URL parsing given value with + // dummyURL as url and opaque path state as state override. + if (auto url = + ada::parse("fake:" + std::string(input), nullptr)) { + // Return the result of URL path serializing dummyURL. + return std::string(url->get_pathname()); + } + // If parseResult is failure, then throw a TypeError. + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_search(std::string_view input) { + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // Set dummyURL’s query to the empty string. + // Let parseResult be the result of running basic URL parser given value with + // dummyURL as url and query state as state override. + auto url = ada::parse("fake://dummy.test", nullptr); + ADA_ASSERT_TRUE(url.has_value()); + url->set_search(input); + if (url->has_search()) { + const auto search = url->get_search(); + return std::string(search.substr(1)); + } + return tl::unexpected(errors::type_error); +} + +tl::expected canonicalize_hash(std::string_view input) { + // If value is the empty string, return value. + if (input.empty()) [[unlikely]] { + return ""; + } + // Let dummyURL be a new URL record. + // Set dummyURL’s fragment to the empty string. + // Let parseResult be the result of running basic URL parser given value with + // dummyURL as url and fragment state as state override. + auto url = ada::parse("fake://dummy.test", nullptr); + ADA_ASSERT_TRUE(url.has_value()); + url->set_hash(input); + // Return dummyURL’s fragment. + if (url->has_hash()) { + const auto hash = url->get_hash(); + return std::string(hash.substr(1)); + } + return tl::unexpected(errors::type_error); +} + +tl::expected, errors> tokenize(std::string_view input, + token_policy policy) { + ada_log("tokenize input: ", input); + // Let tokenizer be a new tokenizer. + // Set tokenizer’s input to input. + // Set tokenizer’s policy to policy. + auto tokenizer = Tokenizer(input, policy); + // While tokenizer’s index is less than tokenizer’s input's code point length: + while (tokenizer.index < tokenizer.input.size()) { + // Run seek and get the next code point given tokenizer and tokenizer’s + // index. + tokenizer.seek_and_get_next_code_point(tokenizer.index); + + // If tokenizer’s code point is U+002A (*): + if (tokenizer.code_point == '*') { + // Run add a token with default position and length given tokenizer and + // "asterisk". + tokenizer.add_token_with_defaults(token_type::ASTERISK); + ada_log("add ASTERISK token"); + // Continue. + continue; + } + + // If tokenizer’s code point is U+002B (+) or U+003F (?): + if (tokenizer.code_point == '+' || tokenizer.code_point == '?') { + // Run add a token with default position and length given tokenizer and + // "other-modifier". + tokenizer.add_token_with_defaults(token_type::OTHER_MODIFIER); + // Continue. + continue; + } + + // If tokenizer’s code point is U+005C (\): + if (tokenizer.code_point == '\\') { + // If tokenizer’s index is equal to tokenizer’s input's code point length + // − 1: + if (tokenizer.index == tokenizer.input.size() - 1) { + // Run process a tokenizing error given tokenizer, tokenizer’s next + // index, and tokenizer’s index. + if (auto error = tokenizer.process_tokenizing_error( + tokenizer.next_index, tokenizer.index)) { + ada_log("process_tokenizing_error failed"); + return tl::unexpected(*error); + } + continue; + } + + // Let escaped index be tokenizer’s next index. + auto escaped_index = tokenizer.next_index; + // Run get the next code point given tokenizer. + tokenizer.get_next_code_point(); + // Run add a token with default length given tokenizer, "escaped-char", + // tokenizer’s next index, and escaped index. + tokenizer.add_token_with_default_length( + token_type::ESCAPED_CHAR, tokenizer.next_index, escaped_index); + ada_log("add ESCAPED_CHAR token on next_index ", tokenizer.next_index, + " with escaped index ", escaped_index); + // Continue. + continue; + } + + // If tokenizer’s code point is U+007B ({): + if (tokenizer.code_point == '{') { + // Run add a token with default position and length given tokenizer and + // "open". + tokenizer.add_token_with_defaults(token_type::OPEN); + ada_log("add OPEN token"); + continue; + } + + // If tokenizer’s code point is U+007D (}): + if (tokenizer.code_point == '}') { + // Run add a token with default position and length given tokenizer and + // "close". + tokenizer.add_token_with_defaults(token_type::CLOSE); + ada_log("add CLOSE token"); + continue; + } + + // If tokenizer’s code point is U+003A (:): + if (tokenizer.code_point == ':') { + // Let name position be tokenizer’s next index. + auto name_position = tokenizer.next_index; + // Let name start be name position. + auto name_start = name_position; + // While name position is less than tokenizer’s input's code point length: + while (name_position < tokenizer.input.size()) { + // Run seek and get the next code point given tokenizer and name + // position. + tokenizer.seek_and_get_next_code_point(name_position); + // Let first code point be true if name position equals name start and + // false otherwise. + bool first_code_point = name_position == name_start; + // Let valid code point be the result of running is a valid name code + // point given tokenizer’s code point and first code point. + auto valid_code_point = + idna::valid_name_code_point(tokenizer.code_point, first_code_point); + ada_log("tokenizer.code_point=", uint32_t(tokenizer.code_point), + " first_code_point=", first_code_point, + " valid_code_point=", valid_code_point); + // If valid code point is false break. + if (!valid_code_point) break; + // Set name position to tokenizer’s next index. + name_position = tokenizer.next_index; + } + + // If name position is less than or equal to name start: + if (name_position <= name_start) { + // Run process a tokenizing error given tokenizer, name start, and + // tokenizer’s index. + if (auto error = tokenizer.process_tokenizing_error(name_start, + tokenizer.index)) { + ada_log("process_tokenizing_error failed"); + return tl::unexpected(*error); + } + // Continue + continue; + } + + // Run add a token with default length given tokenizer, "name", name + // position, and name start. + tokenizer.add_token_with_default_length(token_type::NAME, name_position, + name_start); + continue; + } + + // If tokenizer’s code point is U+0028 ((): + if (tokenizer.code_point == '(') { + // Let depth be 1. + size_t depth = 1; + // Let regexp position be tokenizer’s next index. + auto regexp_position = tokenizer.next_index; + // Let regexp start be regexp position. + auto regexp_start = regexp_position; + // Let error be false. + bool error = false; + + // While regexp position is less than tokenizer’s input's code point + // length: + while (regexp_position < tokenizer.input.size()) { + // Run seek and get the next code point given tokenizer and regexp + // position. + tokenizer.seek_and_get_next_code_point(regexp_position); + + // TODO: Optimization opportunity: The next 2 if statements can be + // merged. If the result of running is ASCII given tokenizer’s code + // point is false: + if (!unicode::is_ascii(tokenizer.code_point)) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer’s index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + // Set error to true. + error = true; + break; + } + + // If regexp position equals regexp start and tokenizer’s code point is + // U+003F (?): + if (regexp_position == regexp_start && tokenizer.code_point == '?') { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer’s index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + // Set error to true; + error = true; + break; + } + + // If tokenizer’s code point is U+005C (\): + if (tokenizer.code_point == '\\') { + // If regexp position equals tokenizer’s input's code point length − 1 + if (regexp_position == tokenizer.input.size() - 1) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer’s index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + // Set error to true. + error = true; + break; + } + // Run get the next code point given tokenizer. + tokenizer.get_next_code_point(); + // If the result of running is ASCII given tokenizer’s code point is + // false: + if (!unicode::is_ascii(tokenizer.code_point)) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer’s index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index); + process_error.has_value()) { + return tl::unexpected(*process_error); + } + // Set error to true. + error = true; + break; + } + // Set regexp position to tokenizer’s next index. + regexp_position = tokenizer.next_index; + continue; + } + + // If tokenizer’s code point is U+0029 ()): + if (tokenizer.code_point == ')') { + // Decrement depth by 1. + depth--; + // If depth is 0: + if (depth == 0) { + // Set regexp position to tokenizer’s next index. + regexp_position = tokenizer.next_index; + // Break. + break; + } + } else if (tokenizer.code_point == '(') { + // Otherwise if tokenizer’s code point is U+0028 ((): + // Increment depth by 1. + depth++; + // If regexp position equals tokenizer’s input's code point length − + // 1: + if (regexp_position == tokenizer.input.size() - 1) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer’s index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + // Set error to true. + error = true; + break; + } + // Let temporary position be tokenizer’s next index. + auto temporary_position = tokenizer.next_index; + // Run get the next code point given tokenizer. + tokenizer.get_next_code_point(); + // If tokenizer’s code point is not U+003F (?): + if (tokenizer.code_point != '?') { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer’s index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + // Set error to true. + error = true; + break; + } + // Set tokenizer’s next index to temporary position. + tokenizer.next_index = temporary_position; + } + // Set regexp position to tokenizer’s next index. + regexp_position = tokenizer.next_index; + } + + // If error is true continue. + if (error) continue; + // If depth is not zero: + if (depth != 0) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer’s index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + return tl::unexpected(*process_error); + } + continue; + } + // Let regexp length be regexp position − regexp start − 1. + auto regexp_length = regexp_position - regexp_start - 1; + // If regexp length is zero: + if (regexp_length == 0) { + // Run process a tokenizing error given tokenizer, regexp start, and + // tokenizer’s index. + if (auto process_error = tokenizer.process_tokenizing_error( + regexp_start, tokenizer.index)) { + ada_log("process_tokenizing_error failed"); + return tl::unexpected(*process_error); + } + continue; + } + // Run add a token given tokenizer, "regexp", regexp position, regexp + // start, and regexp length. + tokenizer.add_token(token_type::REGEXP, regexp_position, regexp_start, + regexp_length); + continue; + } + // Run add a token with default position and length given tokenizer and + // "char". + tokenizer.add_token_with_defaults(token_type::CHAR); + } + // Run add a token with default length given tokenizer, "end", tokenizer’s + // index, and tokenizer’s index. + tokenizer.add_token_with_default_length(token_type::END, tokenizer.index, + tokenizer.index); + + ada_log("tokenizer.token_list size is: ", tokenizer.token_list.size()); + // Return tokenizer’s token list. + return tokenizer.token_list; +} + +std::string escape_pattern_string(std::string_view input) { + ada_log("escape_pattern_string called with input=", input); + if (input.empty()) [[unlikely]] { + return ""; + } + // Assert: input is an ASCII string. + ADA_ASSERT_TRUE(ada::idna::is_ascii(input)); + // Let result be the empty string. + std::string result{}; + result.reserve(input.size()); + + // TODO: Optimization opportunity: Use a lookup table + constexpr auto should_escape = [](const char c) { + return c == '+' || c == '*' || c == '?' || c == ':' || c == '{' || + c == '}' || c == '(' || c == ')' || c == '\\'; + }; + + // While index is less than input’s length: + for (const auto& c : input) { + if (should_escape(c)) { + // then append U+005C (\) to the end of result. + result.append("\\"); + } + + // Append c to the end of result. + result += c; + } + // Return result. + return result; +} + +namespace { +constexpr std::array escape_regexp_table = []() consteval { + std::array out{}; + for (auto& c : {'.', '+', '*', '?', '^', '$', '{', '}', '(', ')', '[', ']', + '|', '/', '\\'}) { + out[c] = 1; + } + return out; +}(); + +constexpr bool should_escape_regexp_char(char c) { + return escape_regexp_table[(uint8_t)c]; +} +} // namespace + +std::string escape_regexp_string(std::string_view input) { + // Assert: input is an ASCII string. + ADA_ASSERT_TRUE(idna::is_ascii(input)); + // Let result be the empty string. + std::string result{}; + result.reserve(input.size()); + for (const auto& c : input) { + // TODO: Optimize this even further + if (should_escape_regexp_char(c)) { + result.append(std::string("\\") + c); + } else { + result.push_back(c); + } + } + return result; +} + +std::string process_base_url_string(std::string_view input, + std::string_view type) { + // If type is not "pattern" return input. + if (type != "pattern") { + return std::string(input); + } + // Return the result of escaping a pattern string given input. + return escape_pattern_string(input); +} + +constexpr bool is_absolute_pathname(std::string_view input, + std::string_view type) noexcept { + // If input is the empty string, then return false. + if (input.empty()) [[unlikely]] { + return false; + } + // If input[0] is U+002F (/), then return true. + if (input.starts_with("/")) return true; + // If type is "url", then return false. + if (type == "url") return false; + // If input’s code point length is less than 2, then return false. + if (input.size() < 2) return false; + // If input[0] is U+005C (\) and input[1] is U+002F (/), then return true. + if (input.starts_with("\\/")) return true; + // If input[0] is U+007B ({) and input[1] is U+002F (/), then return true. + if (input.starts_with("{/")) return true; + // Return false. + return false; +} + +std::string generate_pattern_string( + std::vector& part_list, + url_pattern_compile_component_options& options) { + // Let result be the empty string. + std::string result{}; + // Let index list be the result of getting the indices for part list. + // For each index of index list: + for (size_t index = 0; index < part_list.size(); index++) { + // Let part be part list[index]. + auto part = part_list[index]; + // Let previous part be part list[index - 1] if index is greater than 0, + // otherwise let it be null. + // TODO: Optimization opportunity. Find a way to avoid making a copy here. + std::optional previous_part = + index == 0 ? std::nullopt : std::optional(part_list[index - 1]); + // Let next part be part list[index + 1] if index is less than index list’s + // size - 1, otherwise let it be null. + std::optional next_part = + index < part_list.size() - 1 ? std::optional(part_list[index + 1]) + : std::nullopt; + // If part’s type is "fixed-text" then: + if (part.type == url_pattern_part_type::FIXED_TEXT) { + // If part’s modifier is "none" then: + if (part.modifier == url_pattern_part_modifier::NONE) { + // Append the result of running escape a pattern string given part’s + // value to the end of result. + result.append(escape_pattern_string(part.value)); + continue; + } + // Append "{" to the end of result. + result += "{"; + // Append the result of running escape a pattern string given part’s value + // to the end of result. + result.append(escape_pattern_string(part.value)); + // Append "}" to the end of result. + result += "}"; + // Append the result of running convert a modifier to a string given + // part’s modifier to the end of result. + result.append(convert_modifier_to_string(part.modifier)); + continue; + } + // Let custom name be true if part’s name[0] is not an ASCII digit; + // otherwise false. + bool custom_name = !unicode::is_ascii_digit(part.name[0]); + // Let needs grouping be true if at least one of the following are true, + // otherwise let it be false: + // - part’s suffix is not the empty string. + // - part’s prefix is not the empty string and is not options’s prefix code + // point. + bool needs_grouping = + !part.suffix.empty() || + (!part.prefix.empty() && part.prefix[0] != options.get_prefix()[0]); + + // If all of the following are true: + // - needs grouping is false; and + // - custom name is true; and + // - part’s type is "segment-wildcard"; and + // - part’s modifier is "none"; and + // - next part is not null; and + // - next part’s prefix is the empty string; and + // - next part’s suffix is the empty string + if (!needs_grouping && custom_name && + part.type == url_pattern_part_type::SEGMENT_WILDCARD && + part.modifier == url_pattern_part_modifier::NONE && + next_part.has_value() && next_part->prefix.empty() && + next_part->suffix.empty()) { + // If next part’s type is "fixed-text": + if (next_part->type == url_pattern_part_type::FIXED_TEXT) { + // Set needs grouping to true if the result of running is a valid name + // code point given next part’s value's first code point and the boolean + // false is true. + if (idna::valid_name_code_point(next_part->value[0], false)) { + needs_grouping = true; + } + } else { + // Set needs grouping to true if next part’s name[0] is an ASCII digit. + needs_grouping = !next_part->name.empty() && + unicode::is_ascii_digit(next_part->name[0]); + } + } + + // If all of the following are true: + // - needs grouping is false; and + // - part’s prefix is the empty string; and + // - previous part is not null; and + // - previous part’s type is "fixed-text"; and + // - previous part’s value's last code point is options’s prefix code point. + // then set needs grouping to true. + if (!needs_grouping && part.prefix.empty() && previous_part.has_value() && + previous_part->type == url_pattern_part_type::FIXED_TEXT && + !options.get_prefix().empty() && + previous_part->value.at(previous_part->value.size() - 1) == + options.get_prefix()[0]) { + needs_grouping = true; + } + + // Assert: part’s name is not the empty string or null. + ADA_ASSERT_TRUE(!part.name.empty()); + + // If needs grouping is true, then append "{" to the end of result. + if (needs_grouping) { + result.append("{"); + } + + // Append the result of running escape a pattern string given part’s prefix + // to the end of result. + result.append(escape_pattern_string(part.prefix)); + + // If custom name is true: + if (custom_name) { + // Append ":" to the end of result. + result.append(":"); + // Append part’s name to the end of result. + result.append(part.name); + } + + // If part’s type is "regexp" then: + if (part.type == url_pattern_part_type::REGEXP) { + // Append "(" to the end of result. + result.append("("); + // Append part’s value to the end of result. + result.append(part.value); + // Append ")" to the end of result. + result.append(")"); + } else if (part.type == url_pattern_part_type::SEGMENT_WILDCARD && + !custom_name) { + // Otherwise if part’s type is "segment-wildcard" and custom name is + // false: Append "(" to the end of result. + result.append("("); + // Append the result of running generate a segment wildcard regexp given + // options to the end of result. + result.append(generate_segment_wildcard_regexp(options)); + // Append ")" to the end of result. + result.append(")"); + } else if (part.type == url_pattern_part_type::FULL_WILDCARD) { + // Otherwise if part’s type is "full-wildcard": + // If custom name is false and one of the following is true: + // - previous part is null; or + // - previous part’s type is "fixed-text"; or + // - previous part’s modifier is not "none"; or + // - needs grouping is true; or + // - part’s prefix is not the empty string + // - then append "*" to the end of result. + if (!custom_name && + (!previous_part.has_value() || + previous_part->type == url_pattern_part_type::FIXED_TEXT || + previous_part->modifier != url_pattern_part_modifier::NONE || + needs_grouping || !part.prefix.empty())) { + result.append("*"); + } else { + // Append "(" to the end of result. + // Append full wildcard regexp value to the end of result. + // Append ")" to the end of result. + result.append("(.*)"); + } + } + + // If all of the following are true: + // - part’s type is "segment-wildcard"; and + // - custom name is true; and + // - part’s suffix is not the empty string; and + // - The result of running is a valid name code point given part’s suffix's + // first code point and the boolean false is true then append U+005C (\) to + // the end of result. + if (part.type == url_pattern_part_type::SEGMENT_WILDCARD && custom_name && + !part.suffix.empty() && + idna::valid_name_code_point(part.suffix[0], false)) { + result.append("\\"); + } + + // Append the result of running escape a pattern string given part’s suffix + // to the end of result. + result.append(escape_pattern_string(part.suffix)); + // If needs grouping is true, then append "}" to the end of result. + if (needs_grouping) result.append("}"); + // Append the result of running convert a modifier to a string given part’s + // modifier to the end of result. + result.append(convert_modifier_to_string(part.modifier)); + } + // Return result. + return result; +} +} // namespace ada::url_pattern_helpers +/* end file src/url_pattern_helpers.cpp */ +/* begin file src/url_pattern_regex.cpp */ +#include + +namespace ada::url_pattern_regex { +std::optional std_regex_provider::create_instance( + std::string_view pattern, bool ignore_case) { + // Let flags be an empty string. + // If options’s ignore case is true then set flags to "vi". + // Otherwise set flags to "v" + auto flags = ignore_case + ? std::regex::icase | std::regex_constants::ECMAScript + : std::regex_constants::ECMAScript; + try { + return std::regex(pattern.data(), pattern.size(), flags); + } catch (const std::regex_error& e) { + (void)e; + ada_log("std_regex_provider::create_instance failed:", e.what()); + return std::nullopt; + } +} + +std::optional> std_regex_provider::regex_search( + std::string_view input, const std::regex& pattern) { + std::string input_str( + input.begin(), + input.end()); // Convert string_view to string for regex_search + std::smatch match_result; + if (!std::regex_search(input_str, match_result, pattern, + std::regex_constants::match_any)) { + return std::nullopt; + } + std::vector matches; + if (match_result.empty()) { + return matches; + } + matches.reserve(match_result.size()); + for (size_t i = 1; i < match_result.size(); ++i) { + if (auto entry = match_result[i]; entry.matched) { + matches.emplace_back(entry.str()); + } + } + return matches; +} + +bool std_regex_provider::regex_match(std::string_view input, + const std::regex& pattern) { + return std::regex_match(input.begin(), input.end(), pattern); +} + +} // namespace ada::url_pattern_regex +/* end file src/url_pattern_regex.cpp */ /* begin file src/ada_c.cpp */ ada::result& get_instance(void* result) noexcept { diff --git a/deps/ada/ada.gyp b/deps/ada/ada.gyp index 55cea0033e4fbd..4f871e3f68afb1 100644 --- a/deps/ada/ada.gyp +++ b/deps/ada/ada.gyp @@ -11,7 +11,17 @@ 'direct_dependent_settings': { 'include_dirs': ['.'], }, - 'sources': [ '<@(ada_sources)' ] + 'sources': [ '<@(ada_sources)' ], + 'cflags_cc': ['-fexceptions'], + 'xcode_settings': { + 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', # -fexceptions + }, + 'msvs_settings': { + 'VCCLCompilerTool': { + 'RuntimeTypeInfo': 'true', + 'ExceptionHandling': 1, + }, + }, }, ] } diff --git a/deps/ada/ada.h b/deps/ada/ada.h index 4b00198e6a4bac..7815102686363e 100644 --- a/deps/ada/ada.h +++ b/deps/ada/ada.h @@ -1,4 +1,4 @@ -/* auto-generated on 2024-09-02 20:07:32 -0400. Do not edit! */ +/* auto-generated on 2025-01-23 16:07:04 -0500. Do not edit! */ /* begin file include/ada.h */ /** * @file ada.h @@ -8,7 +8,7 @@ #define ADA_H /* begin file include/ada/ada_idna.h */ -/* auto-generated on 2023-09-19 15:58:51 -0400. Do not edit! */ +/* auto-generated on 2024-12-18 09:44:34 -0500. Do not edit! */ /* begin file include/idna.h */ #ifndef ADA_IDNA_H #define ADA_IDNA_H @@ -129,9 +129,6 @@ std::string to_ascii(std::string_view ut8_string); // https://url.spec.whatwg.org/#forbidden-domain-code-point bool contains_forbidden_domain_code_point(std::string_view ascii_string); -bool begins_with(std::u32string_view view, std::u32string_view prefix); -bool begins_with(std::string_view view, std::string_view prefix); - bool constexpr is_ascii(std::u32string_view view); bool constexpr is_ascii(std::string_view view); @@ -154,20 +151,33 @@ std::string to_unicode(std::string_view input); #endif // ADA_IDNA_TO_UNICODE_H /* end file include/ada/idna/to_unicode.h */ +/* begin file include/ada/idna/identifier.h */ +#ifndef ADA_IDNA_IDENTIFIER_H +#define ADA_IDNA_IDENTIFIER_H + +#include +#include + +namespace ada::idna { + +// Access the first code point of the input string. +// Verify if it is valid name code point given a Unicode code point and a +// boolean first: If first is true return the result of checking if code point +// is contained in the IdentifierStart set of code points. Otherwise return the +// result of checking if code point is contained in the IdentifierPart set of +// code points. Returns false if the input is empty or the code point is not +// valid. There is minimal Unicode error handling: the input should be valid +// UTF-8. https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point +bool valid_name_code_point(char32_t input, bool first); + +} // namespace ada::idna + +#endif +/* end file include/ada/idna/identifier.h */ #endif /* end file include/idna.h */ /* end file include/ada/ada_idna.h */ -/* begin file include/ada/character_sets-inl.h */ -/** - * @file character_sets-inl.h - * @brief Definitions of the character sets used by unicode functions. - * @author Node.js - * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc - */ -#ifndef ADA_CHARACTER_SETS_INL_H -#define ADA_CHARACTER_SETS_INL_H - /* begin file include/ada/character_sets.h */ /** * @file character_sets.h @@ -186,6 +196,10 @@ std::string to_unicode(std::string_view input); #ifndef ADA_COMMON_DEFS_H #define ADA_COMMON_DEFS_H +// https://en.cppreference.com/w/cpp/feature_test#Library_features +// detect C++20 features +#include + #ifdef _MSC_VER #define ADA_VISUAL_STUDIO 1 /** @@ -230,13 +244,6 @@ std::string to_unicode(std::string_view input); #define ada_unused #define ada_warn_unused -#ifndef ada_likely -#define ada_likely(x) x -#endif -#ifndef ada_unlikely -#define ada_unlikely(x) x -#endif - #define ADA_PUSH_DISABLE_WARNINGS __pragma(warning(push)) #define ADA_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0)) #define ADA_DISABLE_VS_WARNING(WARNING_NUMBER) \ @@ -268,13 +275,6 @@ std::string to_unicode(std::string_view input); #define ada_unused __attribute__((unused)) #define ada_warn_unused __attribute__((warn_unused_result)) -#ifndef ada_likely -#define ada_likely(x) __builtin_expect(!!(x), 1) -#endif -#ifndef ada_unlikely -#define ada_unlikely(x) __builtin_expect(!!(x), 0) -#endif - #define ADA_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push") // gcc doesn't seem to disable all warnings with all and extra, add warnings // here as necessary @@ -354,52 +354,6 @@ namespace ada { } } // namespace ada -#if defined(__GNUC__) && !defined(__clang__) -#if __GNUC__ <= 8 -#define ADA_OLD_GCC 1 -#endif // __GNUC__ <= 8 -#endif // defined(__GNUC__) && !defined(__clang__) - -#if ADA_OLD_GCC -#define ada_constexpr -#else -#define ada_constexpr constexpr -#endif - -#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) -#define ADA_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) -#elif defined(_WIN32) -#define ADA_IS_BIG_ENDIAN 0 -#else -#if defined(__APPLE__) || \ - defined(__FreeBSD__) // defined __BYTE_ORDER__ && defined - // __ORDER_BIG_ENDIAN__ -#include -#elif defined(sun) || \ - defined(__sun) // defined(__APPLE__) || defined(__FreeBSD__) -#include -#else // defined(__APPLE__) || defined(__FreeBSD__) - -#ifdef __has_include -#if __has_include() -#include -#endif //__has_include() -#endif //__has_include - -#endif // defined(__APPLE__) || defined(__FreeBSD__) - -#ifndef !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) -#define ADA_IS_BIG_ENDIAN 0 -#endif - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define ADA_IS_BIG_ENDIAN 0 -#else // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define ADA_IS_BIG_ENDIAN 1 -#endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - -#endif // defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ - // Unless the programmer has already set ADA_DEVELOPMENT_CHECKS, // we want to set it under debug builds. We detect a debug build // under Visual Studio when the _DEBUG macro is set. Under the other @@ -491,6 +445,13 @@ namespace ada { #define ada_lifetime_bound #endif +#ifdef __cpp_lib_format +#if __cpp_lib_format >= 202110L +#include +#define ADA_HAS_FORMAT 1 +#endif +#endif + #endif // ADA_COMMON_DEFS_H /* end file include/ada/common_defs.h */ #include @@ -503,11 +464,21 @@ namespace ada { * @brief Includes the definitions for unicode character sets. */ namespace ada::character_sets { -ada_really_inline bool bit_at(const uint8_t a[], uint8_t i); +ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i); } // namespace ada::character_sets #endif // ADA_CHARACTER_SETS_H /* end file include/ada/character_sets.h */ +/* begin file include/ada/character_sets-inl.h */ +/** + * @file character_sets-inl.h + * @brief Definitions of the character sets used by unicode functions. + * @author Node.js + * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc + */ +#ifndef ADA_CHARACTER_SETS_INL_H +#define ADA_CHARACTER_SETS_INL_H + /** * These functions are not part of our public API and may @@ -1012,7 +983,7 @@ constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = { // F8 F9 FA FB FC FD FE FF 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80}; -ada_really_inline bool bit_at(const uint8_t a[], const uint8_t i) { +ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) { return !!(a[i >> 3] & (1 << (i & 7))); } @@ -1028,23 +999,19 @@ ada_really_inline bool bit_at(const uint8_t a[], const uint8_t i) { #ifndef ADA_CHECKERS_INL_H #define ADA_CHECKERS_INL_H - -#include +#include #include -#include namespace ada::checkers { -inline bool has_hex_prefix_unsafe(std::string_view input) { +constexpr bool has_hex_prefix_unsafe(std::string_view input) { // This is actually efficient code, see has_hex_prefix for the assembly. - uint32_t value_one = 1; - bool is_little_endian = (reinterpret_cast(&value_one)[0] == 1); - uint16_t word0x{}; - std::memcpy(&word0x, "0x", 2); // we would use bit_cast in C++20 and the - // function could be constexpr. - uint16_t two_first_bytes{}; - std::memcpy(&two_first_bytes, input.data(), 2); - if (is_little_endian) { + constexpr bool is_little_endian = std::endian::native == std::endian::little; + constexpr uint16_t word0x = 0x7830; + uint16_t two_first_bytes = + static_cast(input[0]) | + static_cast((static_cast(input[1]) << 8)); + if constexpr (is_little_endian) { two_first_bytes |= 0x2000; } else { two_first_bytes |= 0x020; @@ -1052,7 +1019,7 @@ inline bool has_hex_prefix_unsafe(std::string_view input) { return two_first_bytes == word0x; } -inline bool has_hex_prefix(std::string_view input) { +constexpr bool has_hex_prefix(std::string_view input) { return input.size() >= 2 && has_hex_prefix_unsafe(input); } @@ -1064,26 +1031,18 @@ constexpr bool is_alpha(char x) noexcept { return (to_lower(x) >= 'a') && (to_lower(x) <= 'z'); } -inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept { +constexpr bool is_windows_drive_letter(std::string_view input) noexcept { return input.size() >= 2 && (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) && ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' || input[2] == '?' || input[2] == '#')); } -inline constexpr bool is_normalized_windows_drive_letter( +constexpr bool is_normalized_windows_drive_letter( std::string_view input) noexcept { return input.size() >= 2 && (is_alpha(input[0]) && (input[1] == ':')); } -ada_really_inline bool begins_with(std::string_view view, - std::string_view prefix) { - // in C++20, you have view.begins_with(prefix) - // std::equal is constexpr in C++20 - return view.size() >= prefix.size() && - std::equal(prefix.begin(), prefix.end(), view.begin()); -} - } // namespace ada::checkers #endif // ADA_CHECKERS_INL_H @@ -1097,65 +1056,31 @@ ada_really_inline bool begins_with(std::string_view view, #ifndef ADA_LOG_H #define ADA_LOG_H -#include // To enable logging, set ADA_LOGGING to 1: #ifndef ADA_LOGGING #define ADA_LOGGING 0 #endif -namespace ada { - -/** - * Private function used for logging messages. - * @private - */ -template -ada_really_inline void inner_log([[maybe_unused]] T t) { -#if ADA_LOGGING - std::cout << t << std::endl; -#endif -} - -/** - * Private function used for logging messages. - * @private - */ -template -ada_really_inline void inner_log([[maybe_unused]] T t, - [[maybe_unused]] Args... args) { #if ADA_LOGGING - std::cout << t; - inner_log(args...); -#endif -} +#include +#endif // ADA_LOGGING -/** - * Log a message. - * @private - */ -template -ada_really_inline void log([[maybe_unused]] T t, - [[maybe_unused]] Args... args) { -#if ADA_LOGGING - std::cout << "ADA_LOG: " << t; - inner_log(args...); -#endif -} +namespace ada { /** - * Log a message. + * Log a message. If you want to have no overhead when logging is disabled, use + * the ada_log macro. * @private */ -template -ada_really_inline void log([[maybe_unused]] T t) { +template +constexpr ada_really_inline void log([[maybe_unused]] Args... args) { #if ADA_LOGGING - std::cout << "ADA_LOG: " << t << std::endl; -#endif + ((std::cout << "ADA_LOG: ") << ... << args) << std::endl; +#endif // ADA_LOGGING } } // namespace ada #if ADA_LOGGING - #ifndef ada_log #define ada_log(...) \ do { \ @@ -1209,530 +1134,324 @@ ada_warn_unused std::string to_string(encoding_type type); #ifndef ADA_HELPERS_H #define ADA_HELPERS_H -/* begin file include/ada/state.h */ +/* begin file include/ada/url_base.h */ /** - * @file state.h - * @brief Definitions for the states of the URL state machine. + * @file url_base.h + * @brief Declaration for the basic URL definitions */ -#ifndef ADA_STATE_H -#define ADA_STATE_H +#ifndef ADA_URL_BASE_H +#define ADA_URL_BASE_H + +/* begin file include/ada/scheme.h */ +/** + * @file scheme.h + * @brief Declarations for the URL scheme. + */ +#ifndef ADA_SCHEME_H +#define ADA_SCHEME_H #include +/** + * @namespace ada::scheme + * @brief Includes the scheme declarations + */ +namespace ada::scheme { + +/** + * Type of the scheme as an enum. + * Using strings to represent a scheme type is not ideal because + * checking for types involves string comparisons. It is faster to use + * a simple integer. + * In C++11, we are allowed to specify the underlying type of the enum. + * We pick an 8-bit integer (which allows up to 256 types). Specifying the + * type of the enum may help integration with other systems if the type + * variable is exposed (since its value will not depend on the compiler). + */ +enum type : uint8_t { + HTTP = 0, + NOT_SPECIAL = 1, + HTTPS = 2, + WS = 3, + FTP = 4, + WSS = 5, + FILE = 6 +}; + +/** + * A special scheme is an ASCII string that is listed in the first column of the + * following table. The default port for a special scheme is listed in the + * second column on the same row. The default port for any other ASCII string is + * null. + * + * @see https://url.spec.whatwg.org/#url-miscellaneous + * @param scheme + * @return If scheme is a special scheme + */ +ada_really_inline constexpr bool is_special(std::string_view scheme); + +/** + * A special scheme is an ASCII string that is listed in the first column of the + * following table. The default port for a special scheme is listed in the + * second column on the same row. The default port for any other ASCII string is + * null. + * + * @see https://url.spec.whatwg.org/#url-miscellaneous + * @param scheme + * @return The special port + */ +constexpr uint16_t get_special_port(std::string_view scheme) noexcept; + +/** + * Returns the port number of a special scheme. + * @see https://url.spec.whatwg.org/#special-scheme + */ +constexpr uint16_t get_special_port(ada::scheme::type type) noexcept; +/** + * Returns the scheme of an input, or NOT_SPECIAL if it's not a special scheme + * defined by the spec. + */ +constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept; + +} // namespace ada::scheme + +#endif // ADA_SCHEME_H +/* end file include/ada/scheme.h */ + +#include + namespace ada { /** - * @see https://url.spec.whatwg.org/#url-parsing + * Type of URL host as an enum. */ -enum class state { +enum url_host_type : uint8_t { /** - * @see https://url.spec.whatwg.org/#authority-state + * Represents common URLs such as "https://www.google.com" */ - AUTHORITY, - + DEFAULT = 0, /** - * @see https://url.spec.whatwg.org/#scheme-start-state + * Represents ipv4 addresses such as "http://127.0.0.1" */ - SCHEME_START, - + IPV4 = 1, /** - * @see https://url.spec.whatwg.org/#scheme-state + * Represents ipv6 addresses such as + * "http://[2001:db8:3333:4444:5555:6666:7777:8888]" */ - SCHEME, + IPV6 = 2, +}; + +/** + * @brief Base class of URL implementations + * + * @details A url_base contains a few attributes: is_valid, has_opaque_path and + * type. All non-trivial implementation details are in derived classes such as + * ada::url and ada::url_aggregator. + * + * It is an abstract class that cannot be instantiated directly. + */ +struct url_base { + virtual ~url_base() = default; /** - * @see https://url.spec.whatwg.org/#host-state + * Used for returning the validity from the result of the URL parser. */ - HOST, + bool is_valid{true}; /** - * @see https://url.spec.whatwg.org/#no-scheme-state + * A URL has an opaque path if its path is a string. */ - NO_SCHEME, + bool has_opaque_path{false}; /** - * @see https://url.spec.whatwg.org/#fragment-state + * URL hosts type */ - FRAGMENT, + url_host_type host_type = url_host_type::DEFAULT; /** - * @see https://url.spec.whatwg.org/#relative-state + * @private */ - RELATIVE_SCHEME, + ada::scheme::type type{ada::scheme::type::NOT_SPECIAL}; /** - * @see https://url.spec.whatwg.org/#relative-slash-state + * A URL is special if its scheme is a special scheme. A URL is not special if + * its scheme is not a special scheme. */ - RELATIVE_SLASH, + [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept; /** - * @see https://url.spec.whatwg.org/#file-state + * The origin getter steps are to return the serialization of this's URL's + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin */ - FILE, + [[nodiscard]] virtual std::string get_origin() const noexcept = 0; /** - * @see https://url.spec.whatwg.org/#file-host-state + * Returns true if this URL has a valid domain as per RFC 1034 and + * corresponding specifications. Among other things, it requires + * that the domain string has fewer than 255 octets. */ - FILE_HOST, + [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0; /** - * @see https://url.spec.whatwg.org/#file-slash-state + * @private + * + * Return the 'special port' if the URL is special and not 'file'. + * Returns 0 otherwise. */ - FILE_SLASH, + [[nodiscard]] inline uint16_t get_special_port() const noexcept; /** - * @see https://url.spec.whatwg.org/#path-or-authority-state + * @private + * + * Get the default port if the url's scheme has one, returns 0 otherwise. */ - PATH_OR_AUTHORITY, + [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept; /** - * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state - */ - SPECIAL_AUTHORITY_IGNORE_SLASHES, - - /** - * @see https://url.spec.whatwg.org/#special-authority-slashes-state + * @private + * + * Parse a port (16-bit decimal digit) from the provided input. + * We assume that the input does not contain spaces or tabs + * within the ASCII digits. + * It returns how many bytes were consumed when a number is successfully + * parsed. + * @return On failure, it returns zero. + * @see https://url.spec.whatwg.org/#host-parsing */ - SPECIAL_AUTHORITY_SLASHES, + virtual size_t parse_port(std::string_view view, + bool check_trailing_content) noexcept = 0; - /** - * @see https://url.spec.whatwg.org/#special-relative-or-authority-state - */ - SPECIAL_RELATIVE_OR_AUTHORITY, + virtual ada_really_inline size_t parse_port(std::string_view view) noexcept { + return this->parse_port(view, false); + } /** - * @see https://url.spec.whatwg.org/#query-state + * Returns a JSON string representation of this URL. */ - QUERY, + [[nodiscard]] virtual std::string to_string() const = 0; - /** - * @see https://url.spec.whatwg.org/#path-state - */ - PATH, + /** @private */ + virtual inline void clear_pathname() = 0; - /** - * @see https://url.spec.whatwg.org/#path-start-state - */ - PATH_START, + /** @private */ + virtual inline void clear_search() = 0; - /** - * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state - */ - OPAQUE_PATH, + /** @private */ + [[nodiscard]] virtual inline bool has_hash() const noexcept = 0; - /** - * @see https://url.spec.whatwg.org/#port-state - */ - PORT, -}; + /** @private */ + [[nodiscard]] virtual inline bool has_search() const noexcept = 0; -/** - * Stringify a URL state machine state. - */ -ada_warn_unused std::string to_string(ada::state s); +}; // url_base } // namespace ada -#endif // ADA_STATE_H -/* end file include/ada/state.h */ -/* begin file include/ada/url_base.h */ -/** - * @file url_base.h - * @brief Declaration for the basic URL definitions - */ -#ifndef ADA_URL_BASE_H -#define ADA_URL_BASE_H - -/* begin file include/ada/url_components.h */ -/** - * @file url_components.h - * @brief Declaration for the URL Components - */ -#ifndef ADA_URL_COMPONENTS_H -#define ADA_URL_COMPONENTS_H - +#endif +/* end file include/ada/url_base.h */ -#include #include +#include -namespace ada { +#if ADA_DEVELOPMENT_CHECKS +#include +#endif // ADA_DEVELOPMENT_CHECKS /** - * @brief URL Component representations using offsets. - * - * @details We design the url_components struct so that it is as small - * and simple as possible. This version uses 32 bytes. + * These functions are not part of our public API and may + * change at any time. * - * This struct is used to extract components from a single 'href'. + * @private + * @namespace ada::helpers + * @brief Includes the definitions for helper functions */ -struct url_components { - constexpr static uint32_t omitted = uint32_t(-1); - - url_components() = default; - url_components(const url_components &u) = default; - url_components(url_components &&u) noexcept = default; - url_components &operator=(url_components &&u) noexcept = default; - url_components &operator=(const url_components &u) = default; - ~url_components() = default; - - /* - * By using 32-bit integers, we implicitly assume that the URL string - * cannot exceed 4 GB. - * - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - */ - uint32_t protocol_end{0}; - /** - * Username end is not `omitted` by default to make username and password - * getters less costly to implement. - */ - uint32_t username_end{0}; - uint32_t host_start{0}; - uint32_t host_end{0}; - uint32_t port{omitted}; - uint32_t pathname_start{0}; - uint32_t search_start{omitted}; - uint32_t hash_start{omitted}; - - /** - * Check the following conditions: - * protocol_end < username_end < ... < hash_start, - * expect when a value is omitted. It also computes - * a lower bound on the possible string length that may match these - * offsets. - * @return true if the offset values are - * consistent with a possible URL string - */ - [[nodiscard]] bool check_offset_consistency() const noexcept; - - /** - * Converts a url_components to JSON stringified version. - */ - [[nodiscard]] std::string to_string() const; - -}; // struct url_components +namespace ada::helpers { -} // namespace ada -#endif -/* end file include/ada/url_components.h */ -/* begin file include/ada/scheme.h */ /** - * @file scheme.h - * @brief Declarations for the URL scheme. + * @private */ -#ifndef ADA_SCHEME_H -#define ADA_SCHEME_H - +template +void encode_json(std::string_view view, out_iter out); -#include -#include -#include +/** + * @private + * This function is used to prune a fragment from a url, and returning the + * removed string if input has fragment. + * + * @details prune_hash seeks the first '#' and returns everything after it + * as a string_view, and modifies (in place) the input so that it points at + * everything before the '#'. If no '#' is found, the input is left unchanged + * and std::nullopt is returned. + * + * @attention The function is non-allocating and it does not throw. + * @returns Note that the returned string_view might be empty! + */ +ada_really_inline std::optional prune_hash( + std::string_view& input) noexcept; /** - * @namespace ada::scheme - * @brief Includes the scheme declarations + * @private + * Defined by the URL specification, shorten a URLs paths. + * @see https://url.spec.whatwg.org/#shorten-a-urls-path + * @returns Returns true if path is shortened. */ -namespace ada::scheme { +ada_really_inline bool shorten_path(std::string& path, + ada::scheme::type type) noexcept; /** - * Type of the scheme as an enum. - * Using strings to represent a scheme type is not ideal because - * checking for types involves string comparisons. It is faster to use - * a simple integer. - * In C++11, we are allowed to specify the underlying type of the enum. - * We pick an 8-bit integer (which allows up to 256 types). Specifying the - * type of the enum may help integration with other systems if the type - * variable is exposed (since its value will not depend on the compiler). + * @private + * Defined by the URL specification, shorten a URLs paths. + * @see https://url.spec.whatwg.org/#shorten-a-urls-path + * @returns Returns true if path is shortened. */ -enum type : uint8_t { - HTTP = 0, - NOT_SPECIAL = 1, - HTTPS = 2, - WS = 3, - FTP = 4, - WSS = 5, - FILE = 6 -}; +ada_really_inline bool shorten_path(std::string_view& path, + ada::scheme::type type) noexcept; /** - * A special scheme is an ASCII string that is listed in the first column of the - * following table. The default port for a special scheme is listed in the - * second column on the same row. The default port for any other ASCII string is - * null. + * @private * - * @see https://url.spec.whatwg.org/#url-miscellaneous - * @param scheme - * @return If scheme is a special scheme + * Parse the path from the provided input and append to the existing + * (possibly empty) path. The input cannot contain tabs and spaces: it + * is the user's responsibility to check. + * + * The input is expected to be UTF-8. + * + * @see https://url.spec.whatwg.org/ */ -ada_really_inline constexpr bool is_special(std::string_view scheme); +ada_really_inline void parse_prepared_path(std::string_view input, + ada::scheme::type type, + std::string& path); /** - * A special scheme is an ASCII string that is listed in the first column of the - * following table. The default port for a special scheme is listed in the - * second column on the same row. The default port for any other ASCII string is - * null. - * - * @see https://url.spec.whatwg.org/#url-miscellaneous - * @param scheme - * @return The special port + * @private + * Remove and mutate all ASCII tab or newline characters from an input. */ -constexpr uint16_t get_special_port(std::string_view scheme) noexcept; +ada_really_inline void remove_ascii_tab_or_newline(std::string& input) noexcept; /** - * Returns the port number of a special scheme. - * @see https://url.spec.whatwg.org/#special-scheme + * @private + * Return the substring from input going from index pos to the end. + * This function cannot throw. */ -constexpr uint16_t get_special_port(ada::scheme::type type) noexcept; +ada_really_inline constexpr std::string_view substring(std::string_view input, + size_t pos) noexcept; + /** - * Returns the scheme of an input, or NOT_SPECIAL if it's not a special scheme - * defined by the spec. + * @private + * Returns true if the string_view points within the string. */ -constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept; - -} // namespace ada::scheme - -#endif // ADA_SCHEME_H -/* end file include/ada/scheme.h */ - -#include - -namespace ada { - -/** - * Type of URL host as an enum. - */ -enum url_host_type : uint8_t { - /** - * Represents common URLs such as "https://www.google.com" - */ - DEFAULT = 0, - /** - * Represents ipv4 addresses such as "http://127.0.0.1" - */ - IPV4 = 1, - /** - * Represents ipv6 addresses such as - * "http://[2001:db8:3333:4444:5555:6666:7777:8888]" - */ - IPV6 = 2, -}; - -/** - * @brief Base class of URL implementations - * - * @details A url_base contains a few attributes: is_valid, has_opaque_path and - * type. All non-trivial implementation details are in derived classes such as - * ada::url and ada::url_aggregator. - * - * It is an abstract class that cannot be instantiated directly. - */ -struct url_base { - virtual ~url_base() = default; - - /** - * Used for returning the validity from the result of the URL parser. - */ - bool is_valid{true}; - - /** - * A URL has an opaque path if its path is a string. - */ - bool has_opaque_path{false}; - - /** - * URL hosts type - */ - url_host_type host_type = url_host_type::DEFAULT; - - /** - * @private - */ - ada::scheme::type type{ada::scheme::type::NOT_SPECIAL}; - - /** - * A URL is special if its scheme is a special scheme. A URL is not special if - * its scheme is not a special scheme. - */ - [[nodiscard]] ada_really_inline bool is_special() const noexcept; - - /** - * The origin getter steps are to return the serialization of this's URL's - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin - */ - [[nodiscard]] virtual std::string get_origin() const noexcept = 0; - - /** - * Returns true if this URL has a valid domain as per RFC 1034 and - * corresponding specifications. Among other things, it requires - * that the domain string has fewer than 255 octets. - */ - [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0; - - /** - * @private - * - * Return the 'special port' if the URL is special and not 'file'. - * Returns 0 otherwise. - */ - [[nodiscard]] inline uint16_t get_special_port() const noexcept; - - /** - * @private - * - * Get the default port if the url's scheme has one, returns 0 otherwise. - */ - [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept; - - /** - * @private - * - * Parse a port (16-bit decimal digit) from the provided input. - * We assume that the input does not contain spaces or tabs - * within the ASCII digits. - * It returns how many bytes were consumed when a number is successfully - * parsed. - * @return On failure, it returns zero. - * @see https://url.spec.whatwg.org/#host-parsing - */ - virtual size_t parse_port(std::string_view view, - bool check_trailing_content) noexcept = 0; - - virtual ada_really_inline size_t parse_port(std::string_view view) noexcept { - return this->parse_port(view, false); - } - - /** - * Returns a JSON string representation of this URL. - */ - [[nodiscard]] virtual std::string to_string() const = 0; - - /** @private */ - virtual inline void clear_pathname() = 0; - - /** @private */ - virtual inline void clear_search() = 0; - - /** @private */ - [[nodiscard]] virtual inline bool has_hash() const noexcept = 0; - - /** @private */ - [[nodiscard]] virtual inline bool has_search() const noexcept = 0; - -}; // url_base - -} // namespace ada - -#endif -/* end file include/ada/url_base.h */ - -#include -#include - -/** - * These functions are not part of our public API and may - * change at any time. - * - * @private - * @namespace ada::helpers - * @brief Includes the definitions for helper functions - */ -namespace ada::helpers { - -/** - * @private - */ -template -void encode_json(std::string_view view, out_iter out); - -/** - * @private - * This function is used to prune a fragment from a url, and returning the - * removed string if input has fragment. - * - * @details prune_hash seeks the first '#' and returns everything after it - * as a string_view, and modifies (in place) the input so that it points at - * everything before the '#'. If no '#' is found, the input is left unchanged - * and std::nullopt is returned. - * - * @attention The function is non-allocating and it does not throw. - * @returns Note that the returned string_view might be empty! - */ -ada_really_inline std::optional prune_hash( - std::string_view& input) noexcept; - -/** - * @private - * Defined by the URL specification, shorten a URLs paths. - * @see https://url.spec.whatwg.org/#shorten-a-urls-path - * @returns Returns true if path is shortened. - */ -ada_really_inline bool shorten_path(std::string& path, - ada::scheme::type type) noexcept; - -/** - * @private - * Defined by the URL specification, shorten a URLs paths. - * @see https://url.spec.whatwg.org/#shorten-a-urls-path - * @returns Returns true if path is shortened. - */ -ada_really_inline bool shorten_path(std::string_view& path, - ada::scheme::type type) noexcept; - -/** - * @private - * - * Parse the path from the provided input and append to the existing - * (possibly empty) path. The input cannot contain tabs and spaces: it - * is the user's responsibility to check. - * - * The input is expected to be UTF-8. - * - * @see https://url.spec.whatwg.org/ - */ -ada_really_inline void parse_prepared_path(std::string_view input, - ada::scheme::type type, - std::string& path); - -/** - * @private - * Remove and mutate all ASCII tab or newline characters from an input. - */ -ada_really_inline void remove_ascii_tab_or_newline(std::string& input) noexcept; - -/** - * @private - * Return the substring from input going from index pos to the end. - * This function cannot throw. - */ -ada_really_inline std::string_view substring(std::string_view input, - size_t pos) noexcept; - -/** - * @private - * Returns true if the string_view points within the string. - */ -bool overlaps(std::string_view input1, const std::string& input2) noexcept; +bool overlaps(std::string_view input1, const std::string& input2) noexcept; /** * @private * Return the substring from input going from index pos1 to the pos2 (non * included). The length of the substring is pos2 - pos1. */ -ada_really_inline std::string_view substring(const std::string& input, - size_t pos1, - size_t pos2) noexcept { +ada_really_inline constexpr std::string_view substring(std::string_view input, + size_t pos1, + size_t pos2) noexcept { #if ADA_DEVELOPMENT_CHECKS if (pos2 < pos1) { std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")" @@ -1740,7 +1459,7 @@ ada_really_inline std::string_view substring(const std::string& input, abort(); } #endif - return std::string_view(input.data() + pos1, pos2 - pos1); + return input.substr(pos1, pos2 - pos1); } /** @@ -1756,14 +1475,14 @@ ada_really_inline void resize(std::string_view& input, size_t pos) noexcept; * and whether a colon was found outside brackets. Used by the host parser. */ ada_really_inline std::pair get_host_delimiter_location( - const bool is_special, std::string_view& view) noexcept; + bool is_special, std::string_view& view) noexcept; /** * @private * Removes leading and trailing C0 control and whitespace characters from * string. */ -ada_really_inline void trim_c0_whitespace(std::string_view& input) noexcept; +void trim_c0_whitespace(std::string_view& input) noexcept; /** * @private @@ -1867,8 +1586,8 @@ inline int fast_digit_count(uint32_t x) noexcept { #ifndef ADA_PARSER_H #define ADA_PARSER_H -#include #include +#include /* begin file include/ada/expected.h */ /** @@ -4391,6 +4110,62 @@ void swap(expected &lhs, #endif /* end file include/ada/expected.h */ +/* begin file include/ada/url_pattern_regex.h */ +/** + * @file url_search_params.h + * @brief Declaration for the URL Search Params + */ +#ifndef ADA_URL_PATTERN_REGEX_H +#define ADA_URL_PATTERN_REGEX_H + +#include +#include + +namespace ada::url_pattern_regex { + +template +concept regex_concept = requires(T t, std::string_view pattern, + bool ignore_case, std::string_view input) { + // Ensure the class has a type alias 'regex_type' + typename T::regex_type; + + // Function to create a regex instance + { + T::create_instance(pattern, ignore_case) + } -> std::same_as>; + + // Function to perform regex search + { + T::regex_search(input, std::declval()) + } -> std::same_as>>; + + // Function to match regex pattern + { + T::regex_match(input, std::declval()) + } -> std::same_as; + + // Copy constructor + { T(std::declval()) } -> std::same_as; + + // Move constructor + { T(std::declval()) } -> std::same_as; +}; + +class std_regex_provider { + public: + std_regex_provider() = default; + using regex_type = std::regex; + static std::optional create_instance(std::string_view pattern, + bool ignore_case); + static std::optional> regex_search( + std::string_view input, const regex_type& pattern); + static bool regex_match(std::string_view input, const regex_type& pattern); +}; + +} // namespace ada::url_pattern_regex + +#endif // ADA_URL_PATTERN_REGEX_H +/* end file include/ada/url_pattern_regex.h */ /** * @private @@ -4398,6 +4173,11 @@ void swap(expected &lhs, namespace ada { struct url_aggregator; struct url; +template +class url_pattern; +struct url_pattern_options; +struct url_pattern_init; +enum class errors : uint8_t; } // namespace ada /** @@ -4411,7 +4191,7 @@ namespace ada::parser { * parameter that can be used to resolve relative URLs. If the base_url is * provided, the user_input is resolved against the base_url. */ -template +template result_type parse_url(std::string_view user_input, const result_type* base_url = nullptr); @@ -4420,7 +4200,7 @@ extern template url_aggregator parse_url( extern template url parse_url(std::string_view user_input, const url* base_url); -template +template result_type parse_url_impl(std::string_view user_input, const result_type* base_url = nullptr); @@ -4428,517 +4208,498 @@ extern template url_aggregator parse_url_impl( std::string_view user_input, const url_aggregator* base_url); extern template url parse_url_impl(std::string_view user_input, const url* base_url); + +template +tl::expected, errors> parse_url_pattern_impl( + std::variant input, + const std::string_view* base_url, const url_pattern_options* options); + +extern template tl::expected, + errors> +parse_url_pattern_impl(std::variant input, + const std::string_view* base_url, + const url_pattern_options* options); + } // namespace ada::parser #endif // ADA_PARSER_H /* end file include/ada/parser.h */ -/* begin file include/ada/scheme-inl.h */ +/* begin file include/ada/parser-inl.h */ /** - * @file scheme-inl.h - * @brief Definitions for the URL scheme. + * @file parser-inl.h */ -#ifndef ADA_SCHEME_INL_H -#define ADA_SCHEME_INL_H +#ifndef ADA_PARSER_INL_H +#define ADA_PARSER_INL_H +/* begin file include/ada/url_pattern.h */ +/** + * @file url_pattern.h + * @brief Declaration for the URLPattern implementation. + */ +#ifndef ADA_URL_PATTERN_H +#define ADA_URL_PATTERN_H -namespace ada::scheme { - +/* begin file include/ada/implementation.h */ /** - * @namespace ada::scheme::details - * @brief Includes the definitions for scheme specific entities + * @file implementation.h + * @brief Definitions for user facing functions for parsing URL and it's + * components. */ -namespace details { -// for use with is_special and get_special_port -// Spaces, if present, are removed from URL. -constexpr std::string_view is_special_list[] = {"http", " ", "https", "ws", - "ftp", "wss", "file", " "}; -// for use with get_special_port -constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0}; -} // namespace details - -/**** - * @private - * In is_special, get_scheme_type, and get_special_port, we - * use a standard hashing technique to find the index of the scheme in - * the is_special_list. The hashing technique is based on the size of - * the scheme and the first character of the scheme. It ensures that we - * do at most one string comparison per call. If the protocol is - * predictible (e.g., it is always "http"), we can get a better average - * performance by using a simpler approach where we loop and compare - * scheme with all possible protocols starting with the most likely - * protocol. Doing multiple comparisons may have a poor worst case - * performance, however. In this instance, we choose a potentially - * slightly lower best-case performance for a better worst-case - * performance. We can revisit this choice at any time. - * - * Reference: - * Schmidt, Douglas C. "Gperf: A perfect hash function generator." - * More C++ gems 17 (2000). - * - * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function - * - * Reference: https://github.com/ada-url/ada/issues/617 - ****/ - -ada_really_inline constexpr bool is_special(std::string_view scheme) { - if (scheme.empty()) { - return false; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1)); -} -constexpr uint16_t get_special_port(std::string_view scheme) noexcept { - if (scheme.empty()) { - return 0; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { - return details::special_ports[hash_value]; - } else { - return 0; - } -} -constexpr uint16_t get_special_port(ada::scheme::type type) noexcept { - return details::special_ports[int(type)]; -} -constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept { - if (scheme.empty()) { - return ada::scheme::NOT_SPECIAL; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { - return ada::scheme::type(hash_value); - } else { - return ada::scheme::NOT_SPECIAL; - } -} +#ifndef ADA_IMPLEMENTATION_H +#define ADA_IMPLEMENTATION_H -} // namespace ada::scheme +#include +#include -#endif // ADA_SCHEME_INL_H -/* end file include/ada/scheme-inl.h */ -/* begin file include/ada/serializers.h */ +/* begin file include/ada/url.h */ /** - * @file serializers.h - * @brief Definitions for the URL serializers. + * @file url.h + * @brief Declaration for the URL */ -#ifndef ADA_SERIALIZERS_H -#define ADA_SERIALIZERS_H - +#ifndef ADA_URL_H +#define ADA_URL_H -#include +#include #include #include +#include +/* begin file include/ada/checkers.h */ /** - * @namespace ada::serializers - * @brief Includes the definitions for URL serializers - */ -namespace ada::serializers { - -/** - * Finds and returns the longest sequence of 0 values in a ipv6 input. - */ -void find_longest_sequence_of_ipv6_pieces( - const std::array& address, size_t& compress, - size_t& compress_length) noexcept; - -/** - * Serializes an ipv6 address. - * @details An IPv6 address is a 128-bit unsigned integer that identifies a - * network address. - * @see https://url.spec.whatwg.org/#concept-ipv6-serializer + * @file checkers.h + * @brief Declarations for URL specific checkers used within Ada. */ -std::string ipv6(const std::array& address) noexcept; +#ifndef ADA_CHECKERS_H +#define ADA_CHECKERS_H -/** - * Serializes an ipv4 address. - * @details An IPv4 address is a 32-bit unsigned integer that identifies a - * network address. - * @see https://url.spec.whatwg.org/#concept-ipv4-serializer - */ -std::string ipv4(uint64_t address) noexcept; -} // namespace ada::serializers +#include +#include -#endif // ADA_SERIALIZERS_H -/* end file include/ada/serializers.h */ -/* begin file include/ada/unicode.h */ /** - * @file unicode.h - * @brief Definitions for all unicode specific functions. + * These functions are not part of our public API and may + * change at any time. + * @private + * @namespace ada::checkers + * @brief Includes the definitions for validation functions */ -#ifndef ADA_UNICODE_H -#define ADA_UNICODE_H - - -#include -#include +namespace ada::checkers { /** - * Unicode operations. These functions are not part of our public API and may - * change at any time. - * * @private - * @namespace ada::unicode - * @brief Includes the definitions for unicode operations + * Assuming that x is an ASCII letter, this function returns the lower case + * equivalent. + * @details More likely to be inlined by the compiler and constexpr. */ -namespace ada::unicode { +constexpr char to_lower(char x) noexcept; /** * @private - * We receive a UTF-8 string representing a domain name. - * If the string is percent encoded, we apply percent decoding. - * - * Given a domain, we need to identify its labels. - * They are separated by label-separators: - * - * U+002E (.) FULL STOP - * U+FF0E FULLWIDTH FULL STOP - * U+3002 IDEOGRAPHIC FULL STOP - * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP - * - * They are all mapped to U+002E. - * - * We process each label into a string that should not exceed 63 octets. - * If the string is already punycode (starts with "xn--"), then we must - * scan it to look for unallowed code points. - * Otherwise, if the string is not pure ASCII, we need to transcode it - * to punycode by following RFC 3454 which requires us to - * - Map characters (see section 3), - * - Normalize (see section 4), - * - Reject forbidden characters, - * - Check for right-to-left characters and if so, check all requirements (see - * section 6), - * - Optionally reject based on unassigned code points (section 7). - * - * The Unicode standard provides a table of code points with a mapping, a list - * of forbidden code points and so forth. This table is subject to change and - * will vary based on the implementation. For Unicode 15, the table is at - * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt - * If you use ICU, they parse this table and map it to code using a Python - * script. - * - * The resulting strings should not exceed 255 octets according to RFC 1035 - * section 2.3.4. ICU checks for label size and domain size, but these errors - * are ignored. - * - * @see https://url.spec.whatwg.org/#concept-domain-to-ascii + * Returns true if the character is an ASCII letter. Equivalent to std::isalpha + * but more likely to be inlined by the compiler. * + * @attention std::isalpha is not constexpr generally. */ -bool to_ascii(std::optional& out, std::string_view plain, - size_t first_percent); +constexpr bool is_alpha(char x) noexcept; /** * @private - * Checks if the input has tab or newline characters. + * Check whether a string starts with 0x or 0X. The function is only + * safe if input.size() >=2. * - * @attention The has_tabs_or_newline function is a bottleneck and it is simple - * enough that compilers like GCC can 'autovectorize it'. + * @see has_hex_prefix */ -ada_really_inline bool has_tabs_or_newline( - std::string_view user_input) noexcept; - +constexpr bool has_hex_prefix_unsafe(std::string_view input); /** * @private - * Checks if the input is a forbidden host code point. - * @see https://url.spec.whatwg.org/#forbidden-host-code-point + * Check whether a string starts with 0x or 0X. */ -ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept; +constexpr bool has_hex_prefix(std::string_view input); /** * @private - * Checks if the input contains a forbidden domain code point. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + * Check whether x is an ASCII digit. More likely to be inlined than + * std::isdigit. */ -ada_really_inline constexpr bool contains_forbidden_domain_code_point( - const char* input, size_t length) noexcept; +constexpr bool is_digit(char x) noexcept; /** * @private - * Checks if the input contains a forbidden domain code point in which case - * the first bit is set to 1. If the input contains an upper case ASCII letter, - * then the second bit is set to 1. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + * @details A string starts with a Windows drive letter if all of the following + * are true: + * + * - its length is greater than or equal to 2 + * - its first two code points are a Windows drive letter + * - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F + * (?), or U+0023 (#). + * + * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter */ -ada_really_inline constexpr uint8_t -contains_forbidden_domain_code_point_or_upper(const char* input, - size_t length) noexcept; +inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept; /** * @private - * Checks if the input is a forbidden domain code point. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + * @details A normalized Windows drive letter is a Windows drive letter of which + * the second code point is U+003A (:). */ -ada_really_inline constexpr bool is_forbidden_domain_code_point( - char c) noexcept; +inline constexpr bool is_normalized_windows_drive_letter( + std::string_view input) noexcept; /** * @private - * Checks if the input is alphanumeric, '+', '-' or '.' + * Returns true if an input is an ipv4 address. It is assumed that the string + * does not contain uppercase ASCII characters (the input should have been + * lowered cased before calling this function) and is not empty. */ -ada_really_inline constexpr bool is_alnum_plus(char c) noexcept; +ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept; /** * @private - * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex - * digit. An ASCII upper hex digit is an ASCII digit or a code point in the - * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an - * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive. + * Returns a bitset. If the first bit is set, then at least one character needs + * percent encoding. If the second bit is set, a \\ is found. If the third bit + * is set then we have a dot. If the fourth bit is set, then we have a percent + * character. */ -ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept; +ada_really_inline constexpr uint8_t path_signature( + std::string_view input) noexcept; /** * @private - * Checks if the input is a C0 control or space character. - * - * @details A C0 control or space is a C0 control or U+0020 SPACE. - * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION - * SEPARATOR ONE, inclusive. - */ -ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept; - -/** - * @private - * Checks if the input is a ASCII tab or newline character. - * - * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR. - */ -ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept; - -/** - * @private - * @details A double-dot path segment must be ".." or an ASCII case-insensitive - * match for ".%2e", "%2e.", or "%2e%2e". + * Returns true if the length of the domain name and its labels are according to + * the specifications. The length of the domain must be 255 octets (253 + * characters not including the last 2 which are the empty label reserved at the + * end). When the empty label is included (a dot at the end), the domain name + * can have 254 characters. The length of a label must be at least 1 and at most + * 63 characters. + * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034 + * @see https://www.unicode.org/reports/tr46/#ToASCII */ -ada_really_inline ada_constexpr bool is_double_dot_path_segment( +ada_really_inline constexpr bool verify_dns_length( std::string_view input) noexcept; -/** - * @private - * @details A single-dot path segment must be "." or an ASCII case-insensitive - * match for "%2e". - */ -ada_really_inline constexpr bool is_single_dot_path_segment( - std::string_view input) noexcept; +} // namespace ada::checkers +#endif // ADA_CHECKERS_H +/* end file include/ada/checkers.h */ +/* begin file include/ada/url_components.h */ /** - * @private - * @details ipv4 character might contain 0-9 or a-f character ranges. + * @file url_components.h + * @brief Declaration for the URL Components */ -ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept; +#ifndef ADA_URL_COMPONENTS_H +#define ADA_URL_COMPONENTS_H -/** - * @private - * @details Convert hex to binary. Caller is responsible to ensure that - * the parameter is an hexadecimal digit (0-9, A-F, a-f). - */ -ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept; +namespace ada { /** - * @private - * first_percent should be = input.find('%') + * @brief URL Component representations using offsets. * - * @todo It would be faster as noexcept maybe, but it could be unsafe since. - * @author Node.js - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245 - * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom + * @details We design the url_components struct so that it is as small + * and simple as possible. This version uses 32 bytes. + * + * This struct is used to extract components from a single 'href'. */ -std::string percent_decode(std::string_view input, size_t first_percent); +struct url_components { + constexpr static uint32_t omitted = uint32_t(-1); -/** - * @private - * Returns a percent-encoding string whether percent encoding was needed or not. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 - */ -std::string percent_encode(std::string_view input, - const uint8_t character_set[]); -/** - * @private - * Returns a percent-encoded string version of input, while starting the percent - * encoding at the provided index. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 - */ -std::string percent_encode(std::string_view input, - const uint8_t character_set[], size_t index); -/** - * @private - * Returns true if percent encoding was needed, in which case, we store - * the percent-encoded content in 'out'. If the boolean 'append' is set to - * true, the content is appended to 'out'. - * If percent encoding is not needed, out is left unchanged. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 - */ -template -bool percent_encode(std::string_view input, const uint8_t character_set[], - std::string& out); -/** - * @private - * Returns the index at which percent encoding should start, or (equivalently), - * the length of the prefix that does not require percent encoding. - */ -ada_really_inline size_t percent_encode_index(std::string_view input, - const uint8_t character_set[]); -/** - * @private - * Lowers the string in-place, assuming that the content is ASCII. - * Return true if the content was ASCII. - */ -constexpr bool to_lower_ascii(char* input, size_t length) noexcept; -} // namespace ada::unicode + url_components() = default; + url_components(const url_components &u) = default; + url_components(url_components &&u) noexcept = default; + url_components &operator=(url_components &&u) noexcept = default; + url_components &operator=(const url_components &u) = default; + ~url_components() = default; -#endif // ADA_UNICODE_H -/* end file include/ada/unicode.h */ -/* begin file include/ada/url_base-inl.h */ -/** - * @file url_base-inl.h - * @brief Inline functions for url base - */ -#ifndef ADA_URL_BASE_INL_H -#define ADA_URL_BASE_INL_H + /* + * By using 32-bit integers, we implicitly assume that the URL string + * cannot exceed 4 GB. + * + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + */ + uint32_t protocol_end{0}; + /** + * Username end is not `omitted` by default to make username and password + * getters less costly to implement. + */ + uint32_t username_end{0}; + uint32_t host_start{0}; + uint32_t host_end{0}; + uint32_t port{omitted}; + uint32_t pathname_start{0}; + uint32_t search_start{omitted}; + uint32_t hash_start{omitted}; -/* begin file include/ada/url_aggregator.h */ -/** - * @file url_aggregator.h - * @brief Declaration for the basic URL definitions - */ -#ifndef ADA_URL_AGGREGATOR_H -#define ADA_URL_AGGREGATOR_H + /** + * Check the following conditions: + * protocol_end < username_end < ... < hash_start, + * expect when a value is omitted. It also computes + * a lower bound on the possible string length that may match these + * offsets. + * @return true if the offset values are + * consistent with a possible URL string + */ + [[nodiscard]] constexpr bool check_offset_consistency() const noexcept; -#include -#include + /** + * Converts a url_components to JSON stringified version. + */ + [[nodiscard]] std::string to_string() const; +}; // struct url_components +} // namespace ada +#endif +/* end file include/ada/url_components.h */ namespace ada { +struct url_aggregator; + +// namespace parser { +// template +// result_type parse_url(std::string_view user_input, +// const result_type* base_url = nullptr); +// template +// result_type parse_url_impl(std::string_view user_input, +// const result_type* base_url = nullptr); +// } + /** - * @brief Lightweight URL struct. + * @brief Generic URL struct reliant on std::string instantiation. * - * @details The url_aggregator class aims to minimize temporary memory - * allocation while representing a parsed URL. Internally, it contains a single - * normalized URL (the href), and it makes available the components, mostly - * using std::string_view. + * @details To disambiguate from a valid URL string it can also be referred to + * as a URL record. A URL is a struct that represents a universal identifier. + * Unlike the url_aggregator, the ada::url represents the different components + * of a parsed URL as independent std::string instances. This makes the + * structure heavier and more reliant on memory allocations. When getting + * components from the parsed URL, a new std::string is typically constructed. + * + * @see https://url.spec.whatwg.org/#url-representation */ -struct url_aggregator : url_base { - url_aggregator() = default; - url_aggregator(const url_aggregator &u) = default; - url_aggregator(url_aggregator &&u) noexcept = default; - url_aggregator &operator=(url_aggregator &&u) noexcept = default; - url_aggregator &operator=(const url_aggregator &u) = default; - ~url_aggregator() override = default; - - bool set_href(std::string_view input); - bool set_host(std::string_view input); - bool set_hostname(std::string_view input); - bool set_protocol(std::string_view input); - bool set_username(std::string_view input); - bool set_password(std::string_view input); - bool set_port(std::string_view input); - bool set_pathname(std::string_view input); - void set_search(std::string_view input); - void set_hash(std::string_view input); +struct url : url_base { + url() = default; + url(const url &u) = default; + url(url &&u) noexcept = default; + url &operator=(url &&u) noexcept = default; + url &operator=(const url &u) = default; + ~url() override = default; - [[nodiscard]] bool has_valid_domain() const noexcept override; /** - * The origin getter steps are to return the serialization of this's URL's - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin + * @private + * A URL's username is an ASCII string identifying a username. It is initially + * the empty string. */ - [[nodiscard]] std::string get_origin() const noexcept override; + std::string username{}; + /** - * Return the normalized string. - * This function does not allocate memory. - * It is highly efficient. - * @return a constant reference to the underlying normalized URL. - * @see https://url.spec.whatwg.org/#dom-url-href - * @see https://url.spec.whatwg.org/#concept-url-serializer + * @private + * A URL's password is an ASCII string identifying a password. It is initially + * the empty string. */ - [[nodiscard]] inline std::string_view get_href() const noexcept - ada_lifetime_bound; + std::string password{}; + /** - * The username getter steps are to return this's URL's username. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-username + * @private + * A URL's host is null or a host. It is initially null. */ - [[nodiscard]] std::string_view get_username() const noexcept - ada_lifetime_bound; + std::optional host{}; + /** - * The password getter steps are to return this's URL's password. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-password + * @private + * A URL's port is either null or a 16-bit unsigned integer that identifies a + * networking port. It is initially null. */ - [[nodiscard]] std::string_view get_password() const noexcept - ada_lifetime_bound; + std::optional port{}; + /** - * Return this's URL's port, serialized. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-port + * @private + * A URL's path is either an ASCII string or a list of zero or more ASCII + * strings, usually identifying a location. */ - [[nodiscard]] std::string_view get_port() const noexcept ada_lifetime_bound; + std::string path{}; + /** - * Return U+0023 (#), followed by this's URL's fragment. - * This function does not allocate memory. - * @return a lightweight std::string_view.. - * @see https://url.spec.whatwg.org/#dom-url-hash + * @private + * A URL's query is either null or an ASCII string. It is initially null. */ - [[nodiscard]] std::string_view get_hash() const noexcept ada_lifetime_bound; + std::optional query{}; + + /** + * @private + * A URL's fragment is either null or an ASCII string that can be used for + * further processing on the resource the URL's other components identify. It + * is initially null. + */ + std::optional hash{}; + + /** @return true if it has an host but it is the empty string */ + [[nodiscard]] inline bool has_empty_hostname() const noexcept; + /** @return true if the URL has a (non default) port */ + [[nodiscard]] inline bool has_port() const noexcept; + /** @return true if it has a host (included an empty host) */ + [[nodiscard]] inline bool has_hostname() const noexcept; + [[nodiscard]] bool has_valid_domain() const noexcept override; + + /** + * Returns a JSON string representation of this URL. + */ + [[nodiscard]] std::string to_string() const override; + + /** + * @see https://url.spec.whatwg.org/#dom-url-href + * @see https://url.spec.whatwg.org/#concept-url-serializer + */ + [[nodiscard]] ada_really_inline std::string get_href() const noexcept; + + /** + * The origin getter steps are to return the serialization of this's URL's + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin + */ + [[nodiscard]] std::string get_origin() const noexcept override; + + /** + * The protocol getter steps are to return this's URL's scheme, followed by + * U+003A (:). + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#dom-url-protocol + */ + [[nodiscard]] std::string get_protocol() const noexcept; + /** * Return url's host, serialized, followed by U+003A (:) and url's port, * serialized. - * This function does not allocate memory. - * When there is no host, this function returns the empty view. - * @return a lightweight std::string_view. + * When there is no host, this function returns the empty string. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-host */ - [[nodiscard]] std::string_view get_host() const noexcept ada_lifetime_bound; + [[nodiscard]] std::string get_host() const noexcept; + /** * Return this's URL's host, serialized. - * This function does not allocate memory. - * When there is no host, this function returns the empty view. - * @return a lightweight std::string_view. + * When there is no host, this function returns the empty string. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-hostname */ - [[nodiscard]] std::string_view get_hostname() const noexcept - ada_lifetime_bound; + [[nodiscard]] std::string get_hostname() const noexcept; + /** * The pathname getter steps are to return the result of URL path serializing * this's URL. - * This function does not allocate memory. - * @return a lightweight std::string_view. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-pathname */ - [[nodiscard]] std::string_view get_pathname() const noexcept - ada_lifetime_bound; + [[nodiscard]] constexpr std::string_view get_pathname() const noexcept; + /** * Compute the pathname length in bytes without instantiating a view or a * string. * @return size of the pathname in bytes * @see https://url.spec.whatwg.org/#dom-url-pathname */ - [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept; + [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept; + /** * Return U+003F (?), followed by this's URL's query. - * This function does not allocate memory. - * @return a lightweight std::string_view. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-search */ - [[nodiscard]] std::string_view get_search() const noexcept ada_lifetime_bound; + [[nodiscard]] std::string get_search() const noexcept; + /** - * The protocol getter steps are to return this's URL's scheme, followed by - * U+003A (:). - * This function does not allocate memory. - * @return a lightweight std::string_view. + * The username getter steps are to return this's URL's username. + * @return a constant reference to the underlying string. + * @see https://url.spec.whatwg.org/#dom-url-username + */ + [[nodiscard]] const std::string &get_username() const noexcept; + + /** + * @return Returns true on successful operation. + * @see https://url.spec.whatwg.org/#dom-url-username + */ + bool set_username(std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-password + */ + bool set_password(std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + bool set_port(std::string_view input); + + /** + * This function always succeeds. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + void set_hash(std::string_view input); + + /** + * This function always succeeds. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + void set_search(std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + bool set_pathname(std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-host + */ + bool set_host(std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-hostname + */ + bool set_hostname(std::string_view input); + + /** + * @return Returns true on success. * @see https://url.spec.whatwg.org/#dom-url-protocol */ - [[nodiscard]] std::string_view get_protocol() const noexcept - ada_lifetime_bound; + bool set_protocol(std::string_view input); + + /** + * @see https://url.spec.whatwg.org/#dom-url-href + */ + bool set_href(std::string_view input); + + /** + * The password getter steps are to return this's URL's password. + * @return a constant reference to the underlying string. + * @see https://url.spec.whatwg.org/#dom-url-password + */ + [[nodiscard]] const std::string &get_password() const noexcept; + + /** + * Return this's URL's port, serialized. + * @return a newly constructed string representing the port. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + [[nodiscard]] std::string get_port() const noexcept; + + /** + * Return U+0023 (#), followed by this's URL's fragment. + * @return a newly constructed string representing the hash. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + [[nodiscard]] std::string get_hash() const noexcept; /** * A URL includes credentials if its username or password is not the empty @@ -4962,976 +4723,2359 @@ struct url_aggregator : url_base { * * Inspired after servo/url * - * @return a constant reference to the underlying component attribute. + * @return a newly constructed component. * * @see * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 */ - [[nodiscard]] ada_really_inline const ada::url_components &get_components() + [[nodiscard]] ada_really_inline ada::url_components get_components() const noexcept; - /** - * Returns a string representation of this URL. - */ - [[nodiscard]] std::string to_string() const override; - /** - * Returns a string diagram of this URL. - */ - [[nodiscard]] std::string to_diagram() const; - - /** - * Verifies that the parsed URL could be valid. Useful for debugging purposes. - * @return true if the URL is valid, otherwise return true of the offsets are - * possible. - */ - [[nodiscard]] bool validate() const noexcept; - - /** @return true if it has an host but it is the empty string */ - [[nodiscard]] inline bool has_empty_hostname() const noexcept; - /** @return true if it has a host (included an empty host) */ - [[nodiscard]] inline bool has_hostname() const noexcept; - /** @return true if the URL has a non-empty username */ - [[nodiscard]] inline bool has_non_empty_username() const noexcept; - /** @return true if the URL has a non-empty password */ - [[nodiscard]] inline bool has_non_empty_password() const noexcept; - /** @return true if the URL has a (non default) port */ - [[nodiscard]] inline bool has_port() const noexcept; - /** @return true if the URL has a password */ - [[nodiscard]] inline bool has_password() const noexcept; /** @return true if the URL has a hash component */ - [[nodiscard]] inline bool has_hash() const noexcept override; + [[nodiscard]] constexpr bool has_hash() const noexcept override; /** @return true if the URL has a search component */ - [[nodiscard]] inline bool has_search() const noexcept override; - - inline void clear_port(); - inline void clear_hash(); - inline void clear_search() override; + [[nodiscard]] constexpr bool has_search() const noexcept override; private: + friend ada::url ada::parser::parse_url(std::string_view, + const ada::url *); friend ada::url_aggregator ada::parser::parse_url( std::string_view, const ada::url_aggregator *); - friend void ada::helpers::strip_trailing_spaces_from_opaque_path< - ada::url_aggregator>(ada::url_aggregator &url) noexcept; + friend void ada::helpers::strip_trailing_spaces_from_opaque_path( + ada::url &url) noexcept; + + friend ada::url ada::parser::parse_url_impl(std::string_view, + const ada::url *); friend ada::url_aggregator ada::parser::parse_url_impl< ada::url_aggregator, true>(std::string_view, const ada::url_aggregator *); - friend ada::url_aggregator - ada::parser::parse_url_impl( - std::string_view, const ada::url_aggregator *); - std::string buffer{}; - url_components components{}; + inline void update_unencoded_base_hash(std::string_view input); + inline void update_base_hostname(std::string_view input); + inline void update_base_search(std::string_view input, + const uint8_t query_percent_encode_set[]); + inline void update_base_search(std::optional &&input); + inline void update_base_pathname(std::string_view input); + inline void update_base_username(std::string_view input); + inline void update_base_password(std::string_view input); + inline void update_base_port(std::optional input); /** - * Returns true if neither the search, nor the hash nor the pathname - * have been set. - * @return true if the buffer is ready to receive the path. + * Sets the host or hostname according to override condition. + * Return true on success. + * @see https://url.spec.whatwg.org/#hostname-state */ - [[nodiscard]] ada_really_inline bool is_at_path() const noexcept; + template + bool set_host_or_hostname(std::string_view input); - inline void add_authority_slashes_if_needed() noexcept; + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-ipv4-parser + */ + [[nodiscard]] bool parse_ipv4(std::string_view input); /** - * To optimize performance, you may indicate how much memory to allocate - * within this instance. + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-ipv6-parser */ - inline void reserve(uint32_t capacity); + [[nodiscard]] bool parse_ipv6(std::string_view input); - ada_really_inline size_t parse_port( - std::string_view view, bool check_trailing_content) noexcept override; - - ada_really_inline size_t parse_port(std::string_view view) noexcept override { - return this->parse_port(view, false); - } + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-opaque-host-parser + */ + [[nodiscard]] bool parse_opaque_host(std::string_view input); /** - * Return true on success. The 'in_place' parameter indicates whether the - * the string_view input is pointing in the buffer. When in_place is false, - * we must nearly always update the buffer. - * @see https://url.spec.whatwg.org/#concept-ipv4-parser + * A URL's scheme is an ASCII string that identifies the type of URL and can + * be used to dispatch a URL for further processing after parsing. It is + * initially the empty string. We only set non_special_scheme when the scheme + * is non-special, otherwise we avoid constructing string. + * + * Special schemes are stored in ada::scheme::details::is_special_list so we + * typically do not need to store them in each url instance. */ - [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place); + std::string non_special_scheme{}; /** - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-ipv6-parser + * A URL cannot have a username/password/port if its host is null or the empty + * string, or its scheme is "file". */ - [[nodiscard]] bool parse_ipv6(std::string_view input); + [[nodiscard]] inline bool cannot_have_credentials_or_port() const; + + ada_really_inline size_t parse_port( + std::string_view view, bool check_trailing_content) noexcept override; + + ada_really_inline size_t parse_port(std::string_view view) noexcept override { + return this->parse_port(view, false); + } /** + * Parse the host from the provided input. We assume that + * the input does not contain spaces or tabs. Control + * characters and spaces are not trimmed (they should have + * been removed if needed). * Return true on success. - * @see https://url.spec.whatwg.org/#concept-opaque-host-parser + * @see https://url.spec.whatwg.org/#host-parsing */ - [[nodiscard]] bool parse_opaque_host(std::string_view input); + [[nodiscard]] ada_really_inline bool parse_host(std::string_view input); - ada_really_inline void parse_path(std::string_view input); + template + [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input); + + constexpr void clear_pathname() override; + constexpr void clear_search() override; + constexpr void set_protocol_as_file(); /** - * A URL cannot have a username/password/port if its host is null or the empty - * string, or its scheme is "file". + * Parse the path from the provided input. + * Return true on success. Control characters not + * trimmed from the ends (they should have + * been removed if needed). + * + * The input is expected to be UTF-8. + * + * @see https://url.spec.whatwg.org/ */ - [[nodiscard]] inline bool cannot_have_credentials_or_port() const; + ada_really_inline void parse_path(std::string_view input); - template - bool set_host_or_hostname(std::string_view input); + /** + * Set the scheme for this URL. The provided scheme should be a valid + * scheme string, be lower-cased, not contain spaces or tabs. It should + * have no spurious trailing or leading content. + */ + inline void set_scheme(std::string &&new_scheme) noexcept; - ada_really_inline bool parse_host(std::string_view input); + /** + * Take the scheme from another URL. The scheme string is moved from the + * provided url. + */ + constexpr void copy_scheme(ada::url &&u) noexcept; - inline void update_base_authority(std::string_view base_buffer, - const ada::url_components &base); - inline void update_unencoded_base_hash(std::string_view input); - inline void update_base_hostname(std::string_view input); - inline void update_base_search(std::string_view input); - inline void update_base_search(std::string_view input, - const uint8_t *query_percent_encode_set); - inline void update_base_pathname(std::string_view input); - inline void update_base_username(std::string_view input); - inline void append_base_username(std::string_view input); - inline void update_base_password(std::string_view input); - inline void append_base_password(std::string_view input); - inline void update_base_port(uint32_t input); - inline void append_base_pathname(std::string_view input); - [[nodiscard]] inline uint32_t retrieve_base_port() const; - inline void clear_hostname(); - inline void clear_password(); - inline void clear_pathname() override; - [[nodiscard]] inline bool has_dash_dot() const noexcept; - void delete_dash_dot(); - inline void consume_prepared_path(std::string_view input); - template - [[nodiscard]] ada_really_inline bool parse_scheme_with_colon( - std::string_view input); - ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end, - std::string_view input); - [[nodiscard]] inline bool has_authority() const noexcept; - inline void set_protocol_as_file(); - inline void set_scheme(std::string_view new_scheme) noexcept; /** - * Fast function to set the scheme from a view with a colon in the - * buffer, does not change type. + * Take the scheme from another URL. The scheme string is copied from the + * provided url. */ - inline void set_scheme_from_view_with_colon( - std::string_view new_scheme_with_colon) noexcept; - inline void copy_scheme(const url_aggregator &u) noexcept; + constexpr void copy_scheme(const ada::url &u); -}; // url_aggregator +}; // struct url inline std::ostream &operator<<(std::ostream &out, const ada::url &u); } // namespace ada -#endif -/* end file include/ada/url_aggregator.h */ -/* begin file include/ada/checkers.h */ -/** - * @file checkers.h - * @brief Declarations for URL specific checkers used within Ada. - */ -#ifndef ADA_CHECKERS_H -#define ADA_CHECKERS_H +#endif // ADA_URL_H +/* end file include/ada/url.h */ +namespace ada { +enum class errors : uint8_t { type_error }; -#include -#include +template +using result = tl::expected; /** - * These functions are not part of our public API and may - * change at any time. - * @private - * @namespace ada::checkers - * @brief Includes the definitions for validation functions + * The URL parser takes a scalar value string input, with an optional null or + * base URL base (default null). The parser assumes the input is a valid ASCII + * or UTF-8 string. + * + * @param input the string input to analyze (must be valid ASCII or UTF-8) + * @param base_url the optional URL input to use as a base url. + * @return a parsed URL. */ -namespace ada::checkers { +template +ada_warn_unused ada::result parse( + std::string_view input, const result_type* base_url = nullptr); -/** - * @private - * Assuming that x is an ASCII letter, this function returns the lower case - * equivalent. - * @details More likely to be inlined by the compiler and constexpr. - */ -constexpr char to_lower(char x) noexcept; +extern template ada::result parse(std::string_view input, + const url* base_url); +extern template ada::result parse( + std::string_view input, const url_aggregator* base_url); /** - * @private - * Returns true if the character is an ASCII letter. Equivalent to std::isalpha - * but more likely to be inlined by the compiler. - * - * @attention std::isalpha is not constexpr generally. + * Verifies whether the URL strings can be parsed. The function assumes + * that the inputs are valid ASCII or UTF-8 strings. + * @see https://url.spec.whatwg.org/#dom-url-canparse + * @return If URL can be parsed or not. */ -constexpr bool is_alpha(char x) noexcept; +bool can_parse(std::string_view input, + const std::string_view* base_input = nullptr); /** - * @private - * Check whether a string starts with 0x or 0X. The function is only - * safe if input.size() >=2. + * Implementation of the URL pattern parsing algorithm. + * @see https://urlpattern.spec.whatwg.org * - * @see has_hex_prefix - */ -inline bool has_hex_prefix_unsafe(std::string_view input); -/** - * @private - * Check whether a string starts with 0x or 0X. + * @param input valid UTF-8 string or URLPatternInit struct + * @param base_url an optional valid UTF-8 string + * @param options an optional url_pattern_options struct + * @param provider an optional regex provider. if not provided, it will + * use ada::url_pattern_regex::std_regex_provider + * @return url_pattern instance */ -inline bool has_hex_prefix(std::string_view input); +template +ada_warn_unused tl::expected, errors> +parse_url_pattern(std::variant input, + const std::string_view* base_url = nullptr, + const url_pattern_options* options = nullptr); /** - * @private - * Check whether x is an ASCII digit. More likely to be inlined than - * std::isdigit. + * Computes a href string from a file path. The function assumes + * that the input is a valid ASCII or UTF-8 string. + * @return a href string (starts with file:://) */ -constexpr bool is_digit(char x) noexcept; +std::string href_from_file(std::string_view path); +} // namespace ada -/** - * @private - * @details A string starts with a Windows drive letter if all of the following - * are true: - * - * - its length is greater than or equal to 2 - * - its first two code points are a Windows drive letter - * - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F - * (?), or U+0023 (#). - * - * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter - */ -inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept; +#endif // ADA_IMPLEMENTATION_H +/* end file include/ada/implementation.h */ -/** - * @private - * @details A normalized Windows drive letter is a Windows drive letter of which - * the second code point is U+003A (:). - */ -inline constexpr bool is_normalized_windows_drive_letter( - std::string_view input) noexcept; +#include +#include +#include +#include -/** - * @private - * @warning Will be removed when Ada requires C++20. - */ -ada_really_inline bool begins_with(std::string_view view, - std::string_view prefix); +#if ADA_TESTING +#include +#endif // ADA_TESTING -/** - * @private - * Returns true if an input is an ipv4 address. It is assumed that the string - * does not contain uppercase ASCII characters (the input should have been - * lowered cased before calling this function) and is not empty. - */ -ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept; +namespace ada { -/** - * @private - * Returns a bitset. If the first bit is set, then at least one character needs - * percent encoding. If the second bit is set, a \\ is found. If the third bit - * is set then we have a dot. If the fourth bit is set, then we have a percent - * character. - */ -ada_really_inline constexpr uint8_t path_signature( - std::string_view input) noexcept; +namespace parser { +template +tl::expected parse_url_pattern_impl( + std::variant input, + const std::string_view* base_url, const url_pattern_options* options); +} -/** - * @private - * Returns true if the length of the domain name and its labels are according to - * the specifications. The length of the domain must be 255 octets (253 - * characters not including the last 2 which are the empty label reserved at the - * end). When the empty label is included (a dot at the end), the domain name - * can have 254 characters. The length of a label must be at least 1 and at most - * 63 characters. - * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034 - * @see https://www.unicode.org/reports/tr46/#ToASCII - */ -ada_really_inline constexpr bool verify_dns_length( - std::string_view input) noexcept; +// Important: C++20 allows us to use concept rather than `using` or `typedef +// and allows functions with second argument, which is optional (using either +// std::nullopt or a parameter with default value) +template +concept url_pattern_encoding_callback = requires(F f, std::string_view sv) { + { f(sv) } -> std::same_as>; +}; -} // namespace ada::checkers +// A structure providing matching patterns for individual components +// of a URL. When a URLPattern is created, or when a URLPattern is +// used to match or test against a URL, the input can be given as +// either a string or a URLPatternInit struct. If a string is given, +// it will be parsed to create a URLPatternInit. The URLPatternInit +// API is defined as part of the URLPattern specification. +struct url_pattern_init { + // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit + static tl::expected process( + url_pattern_init init, std::string_view type, + std::optional protocol = std::nullopt, + std::optional username = std::nullopt, + std::optional password = std::nullopt, + std::optional hostname = std::nullopt, + std::optional port = std::nullopt, + std::optional pathname = std::nullopt, + std::optional search = std::nullopt, + std::optional hash = std::nullopt); + + // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init + static tl::expected process_protocol( + std::string_view value, std::string_view type); + + // @see https://urlpattern.spec.whatwg.org/#process-username-for-init + static tl::expected process_username( + std::string_view value, std::string_view type); + + // @see https://urlpattern.spec.whatwg.org/#process-password-for-init + static tl::expected process_password( + std::string_view value, std::string_view type); + + // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init + static tl::expected process_hostname( + std::string_view value, std::string_view type); + + // @see https://urlpattern.spec.whatwg.org/#process-port-for-init + static tl::expected process_port( + std::string_view port, std::string_view protocol, std::string_view type); + + // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init + static tl::expected process_pathname( + std::string_view value, std::string_view protocol, std::string_view type); + + // @see https://urlpattern.spec.whatwg.org/#process-search-for-init + static tl::expected process_search( + std::string_view value, std::string_view type); + + // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init + static tl::expected process_hash(std::string_view value, + std::string_view type); -#endif // ADA_CHECKERS_H -/* end file include/ada/checkers.h */ -/* begin file include/ada/url.h */ -/** - * @file url.h - * @brief Declaration for the URL - */ -#ifndef ADA_URL_H -#define ADA_URL_H + [[nodiscard]] std::string to_string() const; -#include -#include -#include -#include -#include -#include + bool operator==(const url_pattern_init&) const; + std::optional protocol{}; + std::optional username{}; + std::optional password{}; + std::optional hostname{}; + std::optional port{}; + std::optional pathname{}; + std::optional search{}; + std::optional hash{}; + std::optional base_url{}; +}; -namespace ada { +enum class url_pattern_part_type : uint8_t { + // The part represents a simple fixed text string. + FIXED_TEXT, + // The part represents a matching group with a custom regular expression. + REGEXP, + // The part represents a matching group that matches code points up to the + // next separator code point. This is typically used for a named group like + // ":foo" that does not have a custom regular expression. + SEGMENT_WILDCARD, + // The part represents a matching group that greedily matches all code points. + // This is typically used for the "*" wildcard matching group. + FULL_WILDCARD, +}; -/** - * @brief Generic URL struct reliant on std::string instantiation. - * - * @details To disambiguate from a valid URL string it can also be referred to - * as a URL record. A URL is a struct that represents a universal identifier. - * Unlike the url_aggregator, the ada::url represents the different components - * of a parsed URL as independent std::string instances. This makes the - * structure heavier and more reliant on memory allocations. When getting - * components from the parsed URL, a new std::string is typically constructed. - * - * @see https://url.spec.whatwg.org/#url-representation - */ -struct url : url_base { - url() = default; - url(const url &u) = default; - url(url &&u) noexcept = default; - url &operator=(url &&u) noexcept = default; - url &operator=(const url &u) = default; - ~url() override = default; - - /** - * @private - * A URL's username is an ASCII string identifying a username. It is initially - * the empty string. - */ - std::string username{}; +enum class url_pattern_part_modifier : uint8_t { + // The part does not have a modifier. + NONE, + // The part has an optional modifier indicated by the U+003F (?) code point. + OPTIONAL, + // The part has a "zero or more" modifier indicated by the U+002A (*) code + // point. + ZERO_OR_MORE, + // The part has a "one or more" modifier indicated by the U+002B (+) code + // point. + ONE_OR_MORE, +}; - /** - * @private - * A URL's password is an ASCII string identifying a password. It is initially - * the empty string. - */ - std::string password{}; +// @see https://urlpattern.spec.whatwg.org/#part +class url_pattern_part { + public: + url_pattern_part(url_pattern_part_type _type, std::string&& _value, + url_pattern_part_modifier _modifier) + : type(_type), value(_value), modifier(_modifier) {} + + url_pattern_part(url_pattern_part_type _type, std::string&& _value, + url_pattern_part_modifier _modifier, std::string&& _name, + std::string&& _prefix, std::string&& _suffix) + : type(_type), + value(_value), + modifier(_modifier), + name(_name), + prefix(_prefix), + suffix(_suffix) {} + // A part has an associated type, a string, which must be set upon creation. + url_pattern_part_type type; + // A part has an associated value, a string, which must be set upon creation. + std::string value; + // A part has an associated modifier a string, which must be set upon + // creation. + url_pattern_part_modifier modifier; + // A part has an associated name, a string, initially the empty string. + std::string name{}; + // A part has an associated prefix, a string, initially the empty string. + std::string prefix{}; + // A part has an associated suffix, a string, initially the empty string. + std::string suffix{}; + + inline bool is_regexp() const noexcept; +}; - /** - * @private - * A URL's host is null or a host. It is initially null. - */ - std::optional host{}; +// @see https://urlpattern.spec.whatwg.org/#options-header +struct url_pattern_compile_component_options { + url_pattern_compile_component_options() = default; + explicit url_pattern_compile_component_options( + std::optional new_delimiter = std::nullopt, + std::optional new_prefix = std::nullopt) + : delimiter(new_delimiter), prefix(new_prefix) {} - /** - * @private - * A URL's port is either null or a 16-bit unsigned integer that identifies a - * networking port. It is initially null. - */ - std::optional port{}; + std::string_view get_delimiter() const ada_warn_unused; + std::string_view get_prefix() const ada_warn_unused; - /** - * @private - * A URL's path is either an ASCII string or a list of zero or more ASCII - * strings, usually identifying a location. - */ - std::string path{}; + // @see https://urlpattern.spec.whatwg.org/#options-ignore-case + bool ignore_case = false; - /** - * @private - * A URL's query is either null or an ASCII string. It is initially null. - */ - std::optional query{}; + static url_pattern_compile_component_options DEFAULT; + static url_pattern_compile_component_options HOSTNAME; + static url_pattern_compile_component_options PATHNAME; - /** - * @private - * A URL's fragment is either null or an ASCII string that can be used for - * further processing on the resource the URL's other components identify. It - * is initially null. - */ - std::optional hash{}; + private: + // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point + std::optional delimiter{}; + // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point + std::optional prefix{}; +}; - /** @return true if it has an host but it is the empty string */ - [[nodiscard]] inline bool has_empty_hostname() const noexcept; - /** @return true if the URL has a (non default) port */ - [[nodiscard]] inline bool has_port() const noexcept; - /** @return true if it has a host (included an empty host) */ - [[nodiscard]] inline bool has_hostname() const noexcept; - [[nodiscard]] bool has_valid_domain() const noexcept override; +// A struct providing the URLPattern matching results for a single +// URL component. The URLPatternComponentResult is only ever used +// as a member attribute of a URLPatternResult struct. The +// URLPatternComponentResult API is defined as part of the URLPattern +// specification. +struct url_pattern_component_result { + std::string input; + std::unordered_map groups; + + bool operator==(const url_pattern_component_result&) const; + +#if ADA_TESTING + friend void PrintTo(const url_pattern_component_result& result, + std::ostream* os) { + *os << "input: '" << result.input << "', group: "; + for (const auto& group : result.groups) { + *os << "(" << group.first << ", " << group.second << ") "; + } + } +#endif // ADA_TESTING +}; - /** - * Returns a JSON string representation of this URL. - */ - [[nodiscard]] std::string to_string() const override; +template +class url_pattern_component { + public: + url_pattern_component() = default; + + // This function explicitly takes a std::string because it is moved. + // To avoid unnecessary copy, move each value while calling the constructor. + url_pattern_component(std::string&& new_pattern, + typename regex_provider::regex_type&& new_regexp, + std::vector&& new_group_name_list, + bool new_has_regexp_groups) + : regexp(std::move(new_regexp)), + pattern(std::move(new_pattern)), + group_name_list(new_group_name_list), + has_regexp_groups(new_has_regexp_groups) {} + + // @see https://urlpattern.spec.whatwg.org/#compile-a-component + template + static tl::expected compile( + std::string_view input, F& encoding_callback, + url_pattern_compile_component_options& options); + + // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result + url_pattern_component_result create_component_match_result( + std::string_view input, std::vector&& exec_result); + + std::string to_string() const; + + typename regex_provider::regex_type regexp{}; + std::string pattern{}; + std::vector group_name_list{}; + bool has_regexp_groups = false; +}; - /** - * @see https://url.spec.whatwg.org/#dom-url-href - * @see https://url.spec.whatwg.org/#concept-url-serializer - */ - [[nodiscard]] ada_really_inline std::string get_href() const noexcept; +using url_pattern_input = std::variant; + +// A struct providing the URLPattern matching results for all +// components of a URL. The URLPatternResult API is defined as +// part of the URLPattern specification. +struct url_pattern_result { + std::vector inputs; + url_pattern_component_result protocol; + url_pattern_component_result username; + url_pattern_component_result password; + url_pattern_component_result hostname; + url_pattern_component_result port; + url_pattern_component_result pathname; + url_pattern_component_result search; + url_pattern_component_result hash; +}; - /** - * The origin getter steps are to return the serialization of this's URL's - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin - */ - [[nodiscard]] std::string get_origin() const noexcept override; +struct url_pattern_options { + bool ignore_case = false; - /** - * The protocol getter steps are to return this's URL's scheme, followed by - * U+003A (:). - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-protocol - */ - [[nodiscard]] std::string get_protocol() const noexcept; + std::string to_string() const; +}; - /** - * Return url's host, serialized, followed by U+003A (:) and url's port, - * serialized. - * When there is no host, this function returns the empty string. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-host - */ - [[nodiscard]] std::string get_host() const noexcept; +// URLPattern is a Web Platform standard API for matching URLs against a +// pattern syntax (think of it as a regular expression for URLs). It is +// defined in https://wicg.github.io/urlpattern. +// More information about the URL Pattern syntax can be found at +// https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API +template +class url_pattern { + public: + url_pattern() = default; /** - * Return this's URL's host, serialized. - * When there is no host, this function returns the empty string. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-hostname + * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec */ - [[nodiscard]] std::string get_hostname() const noexcept; + result> exec(const url_pattern_input& input, + std::string_view* base_url); /** - * The pathname getter steps are to return the result of URL path serializing - * this's URL. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-pathname + * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test */ - [[nodiscard]] std::string_view get_pathname() const noexcept; + result test(const url_pattern_input& input, std::string_view* base_url); /** - * Compute the pathname length in bytes without instantiating a view or a - * string. - * @return size of the pathname in bytes - * @see https://url.spec.whatwg.org/#dom-url-pathname + * @see https://urlpattern.spec.whatwg.org/#url-pattern-match + * This function expects a valid UTF-8 string if input is a string. */ - [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept; + result> match( + const url_pattern_input& input, std::string_view* base_url_string); + + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol + [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username + [[nodiscard]] std::string_view get_username() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password + [[nodiscard]] std::string_view get_password() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname + [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port + [[nodiscard]] std::string_view get_port() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname + [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search + [[nodiscard]] std::string_view get_search() const ada_lifetime_bound; + // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash + [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound; + + // If ignoreCase is true, the JavaScript regular expression created for each + // pattern must use the `vi` flag. Otherwise, they must use the `v` flag. + [[nodiscard]] bool ignore_case() const; + + // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups + [[nodiscard]] bool has_regexp_groups() const; - /** - * Return U+003F (?), followed by this's URL's query. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - [[nodiscard]] std::string get_search() const noexcept; + [[nodiscard]] std::string to_string() const; - /** - * The username getter steps are to return this's URL's username. - * @return a constant reference to the underlying string. - * @see https://url.spec.whatwg.org/#dom-url-username - */ - [[nodiscard]] const std::string &get_username() const noexcept; + url_pattern_component protocol_component{}; + url_pattern_component username_component{}; + url_pattern_component password_component{}; + url_pattern_component hostname_component{}; + url_pattern_component port_component{}; + url_pattern_component pathname_component{}; + url_pattern_component search_component{}; + url_pattern_component hash_component{}; + bool ignore_case_ = false; + + template + friend tl::expected parser::parse_url_pattern_impl( + std::variant input, + const std::string_view* base_url, const url_pattern_options* options); +}; - /** - * @return Returns true on successful operation. - * @see https://url.spec.whatwg.org/#dom-url-username - */ - bool set_username(std::string_view input); +} // namespace ada - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-password - */ - bool set_password(std::string_view input); +#endif +/* end file include/ada/url_pattern.h */ +/* begin file include/ada/url_pattern_helpers.h */ +/** + * @file url_pattern_helpers.h + * @brief Declaration for the URLPattern helpers. + */ +#ifndef ADA_URL_PATTERN_HELPERS_H +#define ADA_URL_PATTERN_HELPERS_H - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-port - */ - bool set_port(std::string_view input); - /** - * This function always succeeds. - * @see https://url.spec.whatwg.org/#dom-url-hash - */ - void set_hash(std::string_view input); +#include +#include +#include - /** - * This function always succeeds. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - void set_search(std::string_view input); +namespace ada { +enum class errors : uint8_t; +} + +namespace ada::url_pattern_helpers { + +// @see https://urlpattern.spec.whatwg.org/#token +enum class token_type : uint8_t { + INVALID_CHAR, // 0 + OPEN, // 1 + CLOSE, // 2 + REGEXP, // 3 + NAME, // 4 + CHAR, // 5 + ESCAPED_CHAR, // 6 + OTHER_MODIFIER, // 7 + ASTERISK, // 8 + END, // 9 +}; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - bool set_pathname(std::string_view input); +std::string to_string(token_type type); - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-host - */ - bool set_host(std::string_view input); +// @see https://urlpattern.spec.whatwg.org/#tokenize-policy +enum class token_policy { + STRICT, + LENIENT, +}; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-hostname - */ - bool set_hostname(std::string_view input); +// @see https://urlpattern.spec.whatwg.org/#tokens +class Token { + public: + Token(token_type _type, size_t _index, std::string&& _value) + : type(_type), index(_index), value(std::move(_value)) {} - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-protocol - */ - bool set_protocol(std::string_view input); + // A token has an associated type, a string, initially "invalid-char". + token_type type = token_type::INVALID_CHAR; - /** - * @see https://url.spec.whatwg.org/#dom-url-href - */ - bool set_href(std::string_view input); + // A token has an associated index, a number, initially 0. It is the position + // of the first code point in the pattern string represented by the token. + size_t index = 0; - /** - * The password getter steps are to return this's URL's password. - * @return a constant reference to the underlying string. - * @see https://url.spec.whatwg.org/#dom-url-password - */ - [[nodiscard]] const std::string &get_password() const noexcept; + // A token has an associated value, a string, initially the empty string. It + // contains the code points from the pattern string represented by the token. + std::string value{}; +}; - /** - * Return this's URL's port, serialized. - * @return a newly constructed string representing the port. - * @see https://url.spec.whatwg.org/#dom-url-port - */ - [[nodiscard]] std::string get_port() const noexcept; +// @see https://urlpattern.spec.whatwg.org/#pattern-parser +template +class url_pattern_parser { + public: + url_pattern_parser(F& encoding_callback_, + std::string_view segment_wildcard_regexp_) + : encoding_callback(encoding_callback_), + segment_wildcard_regexp(segment_wildcard_regexp_) {} + + bool can_continue() const { return index < tokens.size(); } + + // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token + Token* try_consume_token(token_type type); + // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token + Token* try_consume_modifier_token(); + // @see + // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token + Token* try_consume_regexp_or_wildcard_token(Token* name_token); + // @see https://urlpattern.spec.whatwg.org/#consume-text + std::string consume_text(); + // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token + bool consume_required_token(token_type type); + // @see + // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value + std::optional maybe_add_part_from_the_pending_fixed_value() + ada_warn_unused; + // @see https://urlpattern.spec.whatwg.org/#add-a-part + std::optional add_part(std::string_view prefix, Token* name_token, + Token* regexp_or_wildcard_token, + std::string_view suyffix, + Token* modifier_token) ada_warn_unused; + + std::vector tokens{}; + F& encoding_callback; + std::string segment_wildcard_regexp; + std::vector parts{}; + std::string pending_fixed_value{}; + size_t index = 0; + size_t next_numeric_name = 0; +}; - /** - * Return U+0023 (#), followed by this's URL's fragment. - * @return a newly constructed string representing the hash. - * @see https://url.spec.whatwg.org/#dom-url-hash - */ - [[nodiscard]] std::string get_hash() const noexcept; +// @see https://urlpattern.spec.whatwg.org/#tokenizer +class Tokenizer { + public: + explicit Tokenizer(std::string_view new_input, token_policy new_policy) + : input(new_input), policy(new_policy) {} + + // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point + void get_next_code_point(); + + // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point + void seek_and_get_next_code_point(size_t index); + + // @see https://urlpattern.spec.whatwg.org/#add-a-token + + void add_token(token_type type, size_t next_position, size_t value_position, + size_t value_length); + + // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length + void add_token_with_default_length(token_type type, size_t next_position, + size_t value_position); + + // @see + // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length + void add_token_with_defaults(token_type type); + + // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error + std::optional process_tokenizing_error( + size_t next_position, size_t value_position) ada_warn_unused; + + // has an associated input, a pattern string, initially the empty string. + std::string input; + // has an associated policy, a tokenize policy, initially "strict". + token_policy policy; + // has an associated token list, a token list, initially an empty list. + std::vector token_list{}; + // has an associated index, a number, initially 0. + size_t index = 0; + // has an associated next index, a number, initially 0. + size_t next_index = 0; + // has an associated code point, a Unicode code point, initially null. + char32_t code_point{}; +}; - /** - * A URL includes credentials if its username or password is not the empty - * string. - */ - [[nodiscard]] ada_really_inline bool has_credentials() const noexcept; +// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser +template +struct constructor_string_parser { + explicit constructor_string_parser(std::string_view new_input, + std::vector&& new_token_list) + : input(new_input), token_list(std::move(new_token_list)) {} + + // @see https://urlpattern.spec.whatwg.org/#rewind + void rewind(); + + // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix + bool is_hash_prefix(); + + // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix + bool is_search_prefix(); + + // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string + static tl::expected parse(std::string_view input); + + // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state + enum class State { + INIT, + PROTOCOL, + AUTHORITY, + USERNAME, + PASSWORD, + HOSTNAME, + PORT, + PATHNAME, + SEARCH, + HASH, + DONE, + }; - /** - * Useful for implementing efficient serialization for the URL. - * - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - * - * Inspired after servo/url - * - * @return a newly constructed component. - * - * @see - * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 - */ - [[nodiscard]] ada_really_inline ada::url_components get_components() - const noexcept; - /** @return true if the URL has a hash component */ - [[nodiscard]] inline bool has_hash() const noexcept override; - /** @return true if the URL has a search component */ - [[nodiscard]] inline bool has_search() const noexcept override; + // @see https://urlpattern.spec.whatwg.org/#change-state + void change_state(State state, size_t skip); + + // @see https://urlpattern.spec.whatwg.org/#is-a-group-open + bool is_group_open() const; + + // @see https://urlpattern.spec.whatwg.org/#is-a-group-close + bool is_group_close() const; + + // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix + bool is_protocol_suffix(); + + // @see + // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag + std::optional compute_protocol_matches_special_scheme_flag(); + + // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes + bool next_is_authority_slashes(); + + // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator + bool is_an_identity_terminator(); + + // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start + bool is_pathname_start(); + + // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix + bool is_password_prefix(); + + // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open + bool is_an_ipv6_open(); + + // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close + bool is_an_ipv6_close(); + + // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix + bool is_port_prefix(); + + // has an associated input, a string, which must be set upon creation. + std::string input; + // has an associated token list, a token list, which must be set upon + // creation. + std::vector token_list; + // has an associated result, a URLPatternInit, initially set to a new + // URLPatternInit. + url_pattern_init result{}; + // has an associated component start, a number, initially set to 0. + size_t component_start = 0; + // has an associated token index, a number, initially set to 0. + size_t token_index = 0; + // has an associated token increment, a number, initially set to 1. + size_t token_increment = 1; + // has an associated group depth, a number, initially set to 0. + size_t group_depth = 0; + // has an associated hostname IPv6 bracket depth, a number, initially set to + // 0. + size_t hostname_ipv6_bracket_depth = 0; + // has an associated protocol matches a special scheme flag, a boolean, + // initially set to false. + bool protocol_matches_a_special_scheme_flag = false; + // has an associated state, a string, initially set to "init". + State state = State::INIT; private: - friend ada::url ada::parser::parse_url(std::string_view, - const ada::url *); - friend ada::url_aggregator ada::parser::parse_url( - std::string_view, const ada::url_aggregator *); - friend void ada::helpers::strip_trailing_spaces_from_opaque_path( - ada::url &url) noexcept; - - friend ada::url ada::parser::parse_url_impl(std::string_view, - const ada::url *); - friend ada::url_aggregator ada::parser::parse_url_impl< - ada::url_aggregator, true>(std::string_view, const ada::url_aggregator *); - - inline void update_unencoded_base_hash(std::string_view input); - inline void update_base_hostname(std::string_view input); - inline void update_base_search(std::string_view input); - inline void update_base_search(std::string_view input, - const uint8_t query_percent_encode_set[]); - inline void update_base_search(std::optional input); - inline void update_base_pathname(std::string_view input); - inline void update_base_username(std::string_view input); - inline void update_base_password(std::string_view input); - inline void update_base_port(std::optional input); - - /** - * Sets the host or hostname according to override condition. - * Return true on success. - * @see https://url.spec.whatwg.org/#hostname-state - */ - template - bool set_host_or_hostname(std::string_view input); - - /** - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-ipv4-parser - */ - [[nodiscard]] bool parse_ipv4(std::string_view input); - - /** - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-ipv6-parser - */ - [[nodiscard]] bool parse_ipv6(std::string_view input); - - /** - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-opaque-host-parser - */ - [[nodiscard]] bool parse_opaque_host(std::string_view input); - - /** - * A URL's scheme is an ASCII string that identifies the type of URL and can - * be used to dispatch a URL for further processing after parsing. It is - * initially the empty string. We only set non_special_scheme when the scheme - * is non-special, otherwise we avoid constructing string. - * - * Special schemes are stored in ada::scheme::details::is_special_list so we - * typically do not need to store them in each url instance. - */ - std::string non_special_scheme{}; - - /** - * A URL cannot have a username/password/port if its host is null or the empty - * string, or its scheme is "file". - */ - [[nodiscard]] inline bool cannot_have_credentials_or_port() const; - - ada_really_inline size_t parse_port( - std::string_view view, bool check_trailing_content) noexcept override; - - ada_really_inline size_t parse_port(std::string_view view) noexcept override { - return this->parse_port(view, false); - } - - /** - * Take the scheme from another URL. The scheme string is copied from the - * provided url. - */ - inline void copy_scheme(const ada::url &u); + // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char + bool is_non_special_pattern_char(size_t index, std::string_view value); - /** - * Parse the host from the provided input. We assume that - * the input does not contain spaces or tabs. Control - * characters and spaces are not trimmed (they should have - * been removed if needed). - * Return true on success. - * @see https://url.spec.whatwg.org/#host-parsing - */ - [[nodiscard]] ada_really_inline bool parse_host(std::string_view input); + // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token + const Token* get_safe_token(size_t index); - template - [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input); + // @see https://urlpattern.spec.whatwg.org/#make-a-component-string + std::string make_component_string(); +}; - inline void clear_pathname() override; - inline void clear_search() override; - inline void set_protocol_as_file(); +// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol +tl::expected canonicalize_protocol(std::string_view input); - /** - * Parse the path from the provided input. - * Return true on success. Control characters not - * trimmed from the ends (they should have - * been removed if needed). - * - * The input is expected to be UTF-8. - * - * @see https://url.spec.whatwg.org/ - */ - ada_really_inline void parse_path(std::string_view input); +// @see https://wicg.github.io/urlpattern/#canonicalize-a-username +tl::expected canonicalize_username(std::string_view input); - /** - * Set the scheme for this URL. The provided scheme should be a valid - * scheme string, be lower-cased, not contain spaces or tabs. It should - * have no spurious trailing or leading content. - */ - inline void set_scheme(std::string &&new_scheme) noexcept; +// @see https://wicg.github.io/urlpattern/#canonicalize-a-password +tl::expected canonicalize_password(std::string_view input); - /** - * Take the scheme from another URL. The scheme string is moved from the - * provided url. - */ - inline void copy_scheme(ada::url &&u) noexcept; +// @see https://wicg.github.io/urlpattern/#canonicalize-a-password +tl::expected canonicalize_hostname(std::string_view input); -}; // struct url +// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname +tl::expected canonicalize_ipv6_hostname( + std::string_view input); -inline std::ostream &operator<<(std::ostream &out, const ada::url &u); -} // namespace ada +// @see https://wicg.github.io/urlpattern/#canonicalize-a-port +tl::expected canonicalize_port(std::string_view input); -#endif // ADA_URL_H -/* end file include/ada/url.h */ +// @see https://wicg.github.io/urlpattern/#canonicalize-a-port +tl::expected canonicalize_port_with_protocol( + std::string_view input, std::string_view protocol); -#include -#include -#if ADA_REGULAR_VISUAL_STUDIO -#include -#endif // ADA_REGULAR_VISUAL_STUDIO +// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname +tl::expected canonicalize_pathname(std::string_view input); -namespace ada { +// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname +tl::expected canonicalize_opaque_pathname( + std::string_view input); -[[nodiscard]] ada_really_inline bool url_base::is_special() const noexcept { - return type != ada::scheme::NOT_SPECIAL; -} +// @see https://wicg.github.io/urlpattern/#canonicalize-a-search +tl::expected canonicalize_search(std::string_view input); -[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept { - return ada::scheme::get_special_port(type); -} +// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash +tl::expected canonicalize_hash(std::string_view input); -[[nodiscard]] ada_really_inline uint16_t -url_base::scheme_default_port() const noexcept { - return scheme::get_special_port(type); -} +// @see https://urlpattern.spec.whatwg.org/#tokenize +tl::expected, errors> tokenize(std::string_view input, + token_policy policy); -} // namespace ada +// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string +std::string process_base_url_string(std::string_view input, + std::string_view type); -#endif // ADA_URL_BASE_INL_H -/* end file include/ada/url_base-inl.h */ -/* begin file include/ada/url-inl.h */ -/** - * @file url-inl.h - * @brief Definitions for the URL - */ -#ifndef ADA_URL_INL_H -#define ADA_URL_INL_H +// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string +std::string escape_pattern_string(std::string_view input); +// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string +std::string escape_regexp_string(std::string_view input); -#include -#include -#if ADA_REGULAR_VISUAL_STUDIO -#include -#endif // ADA_REGULAR_VISUAL_STUDIO +// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname +constexpr bool is_absolute_pathname(std::string_view input, + std::string_view type) noexcept; -namespace ada { -[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept { - return !username.empty() || !password.empty(); -} -[[nodiscard]] ada_really_inline bool url::has_port() const noexcept { - return port.has_value(); -} -[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const { - return !host.has_value() || host.value().empty() || - type == ada::scheme::type::FILE; -} -[[nodiscard]] inline bool url::has_empty_hostname() const noexcept { - if (!host.has_value()) { - return false; - } - return host.value().empty(); -} -[[nodiscard]] inline bool url::has_hostname() const noexcept { - return host.has_value(); -} -inline std::ostream &operator<<(std::ostream &out, const ada::url &u) { - return out << u.to_string(); -} +// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string +template +tl::expected, errors> parse_pattern_string( + std::string_view input, url_pattern_compile_component_options& options, + F& encoding_callback); -[[nodiscard]] size_t url::get_pathname_length() const noexcept { - return path.size(); -} +// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string +std::string generate_pattern_string( + std::vector& part_list, + url_pattern_compile_component_options& options); -[[nodiscard]] ada_really_inline ada::url_components url::get_components() - const noexcept { - url_components out{}; +// @see +// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list +std::tuple> +generate_regular_expression_and_name_list( + std::vector& part_list, + url_pattern_compile_component_options options); - // protocol ends with ':'. for example: "https:" - out.protocol_end = uint32_t(get_protocol().size()); +// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address +bool is_ipv6_address(std::string_view input) noexcept; - // Trailing index is always the next character of the current one. - size_t running_index = out.protocol_end; +// @see +// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme +template +bool protocol_component_matches_special_scheme( + ada::url_pattern_component& input); - if (host.has_value()) { - // 2 characters for "//" and 1 character for starting index - out.host_start = out.protocol_end + 2; +// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string +std::string convert_modifier_to_string(url_pattern_part_modifier modifier); - if (has_credentials()) { - out.username_end = uint32_t(out.host_start + username.size()); +// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp +std::string generate_segment_wildcard_regexp( + url_pattern_compile_component_options options); - out.host_start += uint32_t(username.size()); +} // namespace ada::url_pattern_helpers - if (!password.empty()) { - out.host_start += uint32_t(password.size() + 1); - } +#endif +/* end file include/ada/url_pattern_helpers.h */ - out.host_end = uint32_t(out.host_start + host.value().size()); - } else { - out.username_end = out.host_start; +#include +#include - // Host does not start with "@" if it does not include credentials. - out.host_end = uint32_t(out.host_start + host.value().size()) - 1; +namespace ada::parser { +template +tl::expected, errors> parse_url_pattern_impl( + std::variant input, + const std::string_view* base_url, const url_pattern_options* options) { + // Let init be null. + url_pattern_init init; + + // If input is a scalar value string then: + if (std::holds_alternative(input)) { + // Set init to the result of running parse a constructor string given input. + auto parse_result = + url_pattern_helpers::constructor_string_parser::parse( + std::get(input)); + if (!parse_result) { + ada_log("constructor_string_parser::parse failed"); + return tl::unexpected(parse_result.error()); + } + init = std::move(*parse_result); + // If baseURL is null and init["protocol"] does not exist, then throw a + // TypeError. + if (!base_url && !init.protocol) { + ada_log("base url is null and protocol is not set"); + return tl::unexpected(errors::type_error); } - running_index = out.host_end + 1; + // If baseURL is not null, set init["baseURL"] to baseURL. + if (base_url) { + init.base_url = std::string(*base_url); + } } else { - // Update host start and end date to the same index, since it does not - // exist. - out.host_start = out.protocol_end; - out.host_end = out.host_start; - - if (!has_opaque_path && checkers::begins_with(path, "//")) { - // If url's host is null, url does not have an opaque path, url's path's - // size is greater than 1, and url's path[0] is the empty string, then - // append U+002F (/) followed by U+002E (.) to output. - running_index = out.protocol_end + 2; - } else { - running_index = out.protocol_end; + // Assert: input is a URLPatternInit. + ADA_ASSERT_TRUE(std::holds_alternative(input)); + // If baseURL is not null, then throw a TypeError. + if (base_url) { + ada_log("base url is not null"); + return tl::unexpected(errors::type_error); + } + // Optimization: Avoid copy by moving the input value. + // Set init to input. + init = std::move(std::get(input)); + } + + // Let processedInit be the result of process a URLPatternInit given init, + // "pattern", null, null, null, null, null, null, null, and null. + // TODO: Make "pattern" an enum to avoid creating a string everytime. + auto processed_init = url_pattern_init::process(init, "pattern"); + if (!processed_init) { + ada_log("url_pattern_init::process failed for init and 'pattern'"); + return tl::unexpected(processed_init.error()); + } + + // For each componentName of « "protocol", "username", "password", "hostname", + // "port", "pathname", "search", "hash" If processedInit[componentName] does + // not exist, then set processedInit[componentName] to "*". + ADA_ASSERT_TRUE(processed_init.has_value()); + if (!processed_init->protocol) processed_init->protocol = "*"; + if (!processed_init->username) processed_init->username = "*"; + if (!processed_init->password) processed_init->password = "*"; + if (!processed_init->hostname) processed_init->hostname = "*"; + if (!processed_init->port) processed_init->port = "*"; + if (!processed_init->pathname) processed_init->pathname = "*"; + if (!processed_init->search) processed_init->search = "*"; + if (!processed_init->hash) processed_init->hash = "*"; + + ada_log("-- processed_init->protocol: ", processed_init->protocol.value()); + ada_log("-- processed_init->username: ", processed_init->username.value()); + ada_log("-- processed_init->password: ", processed_init->password.value()); + ada_log("-- processed_init->hostname: ", processed_init->hostname.value()); + ada_log("-- processed_init->port: ", processed_init->port.value()); + ada_log("-- processed_init->pathname: ", processed_init->pathname.value()); + ada_log("-- processed_init->search: ", processed_init->search.value()); + ada_log("-- processed_init->hash: ", processed_init->hash.value()); + + // If processedInit["protocol"] is a special scheme and processedInit["port"] + // is a string which represents its corresponding default port in radix-10 + // using ASCII digits then set processedInit["port"] to the empty string. + // TODO: Optimization opportunity. + if (scheme::is_special(*processed_init->protocol)) { + std::string_view port = processed_init->port.value(); + helpers::trim_c0_whitespace(port); + if (std::to_string(scheme::get_special_port(*processed_init->protocol)) == + port) { + processed_init->port->clear(); } } - if (port.has_value()) { - out.port = *port; - running_index += helpers::fast_digit_count(*port) + 1; // Port omits ':' - } - - out.pathname_start = uint32_t(running_index); - - running_index += path.size(); + // Let urlPattern be a new URL pattern. + url_pattern url_pattern_{}; + + // Set urlPattern’s protocol component to the result of compiling a component + // given processedInit["protocol"], canonicalize a protocol, and default + // options. + auto protocol_component = url_pattern_component::compile( + processed_init->protocol.value(), + url_pattern_helpers::canonicalize_protocol, + url_pattern_compile_component_options::DEFAULT); + if (!protocol_component) { + ada_log("url_pattern_component::compile failed for protocol ", + processed_init->protocol.value()); + return tl::unexpected(protocol_component.error()); + } + url_pattern_.protocol_component = std::move(*protocol_component); + + // Set urlPattern’s username component to the result of compiling a component + // given processedInit["username"], canonicalize a username, and default + // options. + auto username_component = url_pattern_component::compile( + processed_init->username.value(), + url_pattern_helpers::canonicalize_username, + url_pattern_compile_component_options::DEFAULT); + if (!username_component) { + ada_log("url_pattern_component::compile failed for username ", + processed_init->username.value()); + return tl::unexpected(username_component.error()); + } + url_pattern_.username_component = std::move(*username_component); + + // Set urlPattern’s password component to the result of compiling a component + // given processedInit["password"], canonicalize a password, and default + // options. + auto password_component = url_pattern_component::compile( + processed_init->password.value(), + url_pattern_helpers::canonicalize_password, + url_pattern_compile_component_options::DEFAULT); + if (!password_component) { + ada_log("url_pattern_component::compile failed for password ", + processed_init->password.value()); + return tl::unexpected(password_component.error()); + } + url_pattern_.password_component = std::move(*password_component); + + // TODO: Optimization opportunity. The following if statement can be + // simplified. + // If the result running hostname pattern is an IPv6 address given + // processedInit["hostname"] is true, then set urlPattern’s hostname component + // to the result of compiling a component given processedInit["hostname"], + // canonicalize an IPv6 hostname, and hostname options. + if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) { + ada_log("processed_init->hostname is ipv6 address"); + // then set urlPattern’s hostname component to the result of compiling a + // component given processedInit["hostname"], canonicalize an IPv6 hostname, + // and hostname options. + auto hostname_component = url_pattern_component::compile( + processed_init->hostname.value(), + url_pattern_helpers::canonicalize_ipv6_hostname, + url_pattern_compile_component_options::DEFAULT); + if (!hostname_component) { + ada_log("url_pattern_component::compile failed for ipv6 hostname ", + processed_init->hostname.value()); + return tl::unexpected(hostname_component.error()); + } + url_pattern_.hostname_component = std::move(*hostname_component); + } else { + // Otherwise, set urlPattern’s hostname component to the result of compiling + // a component given processedInit["hostname"], canonicalize a hostname, and + // hostname options. + auto hostname_component = url_pattern_component::compile( + processed_init->hostname.value(), + url_pattern_helpers::canonicalize_hostname, + url_pattern_compile_component_options::HOSTNAME); + if (!hostname_component) { + ada_log("url_pattern_component::compile failed for hostname ", + processed_init->hostname.value()); + return tl::unexpected(hostname_component.error()); + } + url_pattern_.hostname_component = std::move(*hostname_component); + } + + // Set urlPattern’s port component to the result of compiling a component + // given processedInit["port"], canonicalize a port, and default options. + auto port_component = url_pattern_component::compile( + processed_init->port.value(), url_pattern_helpers::canonicalize_port, + url_pattern_compile_component_options::DEFAULT); + if (!port_component) { + ada_log("url_pattern_component::compile failed for port ", + processed_init->port.value()); + return tl::unexpected(port_component.error()); + } + url_pattern_.port_component = std::move(*port_component); + + // Let compileOptions be a copy of the default options with the ignore case + // property set to options["ignoreCase"]. + auto compile_options = url_pattern_compile_component_options::DEFAULT; + if (options) { + compile_options.ignore_case = options->ignore_case; + } + + // TODO: Optimization opportunity: Simplify this if statement. + // If the result of running protocol component matches a special scheme given + // urlPattern’s protocol component is true, then: + if (url_pattern_helpers::protocol_component_matches_special_scheme< + regex_provider>(url_pattern_.protocol_component)) { + // Let pathCompileOptions be copy of the pathname options with the ignore + // case property set to options["ignoreCase"]. + auto path_compile_options = url_pattern_compile_component_options::PATHNAME; + if (options) { + path_compile_options.ignore_case = options->ignore_case; + } - if (query.has_value()) { - out.search_start = uint32_t(running_index); - running_index += get_search().size(); - if (get_search().empty()) { - running_index++; + // Set urlPattern’s pathname component to the result of compiling a + // component given processedInit["pathname"], canonicalize a pathname, and + // pathCompileOptions. + auto pathname_component = url_pattern_component::compile( + processed_init->pathname.value(), + url_pattern_helpers::canonicalize_pathname, path_compile_options); + if (!pathname_component) { + ada_log("url_pattern_component::compile failed for pathname ", + processed_init->pathname.value()); + return tl::unexpected(pathname_component.error()); } - } + url_pattern_.pathname_component = std::move(*pathname_component); + } else { + // Otherwise set urlPattern’s pathname component to the result of compiling + // a component given processedInit["pathname"], canonicalize an opaque + // pathname, and compileOptions. + auto pathname_component = url_pattern_component::compile( + processed_init->pathname.value(), + url_pattern_helpers::canonicalize_opaque_pathname, compile_options); + if (!pathname_component) { + ada_log("url_pattern_component::compile failed for opaque pathname ", + processed_init->pathname.value()); + return tl::unexpected(pathname_component.error()); + } + url_pattern_.pathname_component = std::move(*pathname_component); + } + + // Set urlPattern’s search component to the result of compiling a component + // given processedInit["search"], canonicalize a search, and compileOptions. + auto search_component = url_pattern_component::compile( + processed_init->search.value(), url_pattern_helpers::canonicalize_search, + compile_options); + if (!search_component) { + ada_log("url_pattern_component::compile failed for search ", + processed_init->search.value()); + return tl::unexpected(search_component.error()); + } + url_pattern_.search_component = std::move(*search_component); + + // Set urlPattern’s hash component to the result of compiling a component + // given processedInit["hash"], canonicalize a hash, and compileOptions. + auto hash_component = url_pattern_component::compile( + processed_init->hash.value(), url_pattern_helpers::canonicalize_hash, + compile_options); + if (!hash_component) { + ada_log("url_pattern_component::compile failed for hash ", + processed_init->hash.value()); + return tl::unexpected(hash_component.error()); + } + url_pattern_.hash_component = std::move(*hash_component); + + // Return urlPattern. + return url_pattern_; +} + +template tl::expected, + errors> +parse_url_pattern_impl(std::variant input, + const std::string_view* base_url, + const url_pattern_options* options); +} // namespace ada::parser - if (hash.has_value()) { - out.hash_start = uint32_t(running_index); - } +#endif // ADA_PARSER_INL_H +/* end file include/ada/parser-inl.h */ +/* begin file include/ada/scheme-inl.h */ +/** + * @file scheme-inl.h + * @brief Definitions for the URL scheme. + */ +#ifndef ADA_SCHEME_INL_H +#define ADA_SCHEME_INL_H - return out; -} -inline void url::update_base_hostname(std::string_view input) { host = input; } +namespace ada::scheme { -inline void url::update_unencoded_base_hash(std::string_view input) { - // We do the percent encoding - hash = unicode::percent_encode(input, - ada::character_sets::FRAGMENT_PERCENT_ENCODE); -} +/** + * @namespace ada::scheme::details + * @brief Includes the definitions for scheme specific entities + */ +namespace details { +// for use with is_special and get_special_port +// Spaces, if present, are removed from URL. +constexpr std::string_view is_special_list[] = {"http", " ", "https", "ws", + "ftp", "wss", "file", " "}; +// for use with get_special_port +constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0}; +} // namespace details + +/**** + * @private + * In is_special, get_scheme_type, and get_special_port, we + * use a standard hashing technique to find the index of the scheme in + * the is_special_list. The hashing technique is based on the size of + * the scheme and the first character of the scheme. It ensures that we + * do at most one string comparison per call. If the protocol is + * predictible (e.g., it is always "http"), we can get a better average + * performance by using a simpler approach where we loop and compare + * scheme with all possible protocols starting with the most likely + * protocol. Doing multiple comparisons may have a poor worst case + * performance, however. In this instance, we choose a potentially + * slightly lower best-case performance for a better worst-case + * performance. We can revisit this choice at any time. + * + * Reference: + * Schmidt, Douglas C. "Gperf: A perfect hash function generator." + * More C++ gems 17 (2000). + * + * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function + * + * Reference: https://github.com/ada-url/ada/issues/617 + ****/ + +ada_really_inline constexpr bool is_special(std::string_view scheme) { + if (scheme.empty()) { + return false; + } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1)); +} +constexpr uint16_t get_special_port(std::string_view scheme) noexcept { + if (scheme.empty()) { + return 0; + } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { + return details::special_ports[hash_value]; + } else { + return 0; + } +} +constexpr uint16_t get_special_port(ada::scheme::type type) noexcept { + return details::special_ports[int(type)]; +} +constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept { + if (scheme.empty()) { + return ada::scheme::NOT_SPECIAL; + } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { + return ada::scheme::type(hash_value); + } else { + return ada::scheme::NOT_SPECIAL; + } +} + +} // namespace ada::scheme + +#endif // ADA_SCHEME_INL_H +/* end file include/ada/scheme-inl.h */ +/* begin file include/ada/serializers.h */ +/** + * @file serializers.h + * @brief Definitions for the URL serializers. + */ +#ifndef ADA_SERIALIZERS_H +#define ADA_SERIALIZERS_H + + +#include +#include + +/** + * @namespace ada::serializers + * @brief Includes the definitions for URL serializers + */ +namespace ada::serializers { + +/** + * Finds and returns the longest sequence of 0 values in a ipv6 input. + */ +void find_longest_sequence_of_ipv6_pieces( + const std::array& address, size_t& compress, + size_t& compress_length) noexcept; + +/** + * Serializes an ipv6 address. + * @details An IPv6 address is a 128-bit unsigned integer that identifies a + * network address. + * @see https://url.spec.whatwg.org/#concept-ipv6-serializer + */ +std::string ipv6(const std::array& address) noexcept; + +/** + * Serializes an ipv4 address. + * @details An IPv4 address is a 32-bit unsigned integer that identifies a + * network address. + * @see https://url.spec.whatwg.org/#concept-ipv4-serializer + */ +std::string ipv4(uint64_t address) noexcept; + +} // namespace ada::serializers + +#endif // ADA_SERIALIZERS_H +/* end file include/ada/serializers.h */ +/* begin file include/ada/state.h */ +/** + * @file state.h + * @brief Definitions for the states of the URL state machine. + */ +#ifndef ADA_STATE_H +#define ADA_STATE_H + + +#include + +namespace ada { + +/** + * @see https://url.spec.whatwg.org/#url-parsing + */ +enum class state { + /** + * @see https://url.spec.whatwg.org/#authority-state + */ + AUTHORITY, + + /** + * @see https://url.spec.whatwg.org/#scheme-start-state + */ + SCHEME_START, + + /** + * @see https://url.spec.whatwg.org/#scheme-state + */ + SCHEME, + + /** + * @see https://url.spec.whatwg.org/#host-state + */ + HOST, + + /** + * @see https://url.spec.whatwg.org/#no-scheme-state + */ + NO_SCHEME, + + /** + * @see https://url.spec.whatwg.org/#fragment-state + */ + FRAGMENT, + + /** + * @see https://url.spec.whatwg.org/#relative-state + */ + RELATIVE_SCHEME, + + /** + * @see https://url.spec.whatwg.org/#relative-slash-state + */ + RELATIVE_SLASH, + + /** + * @see https://url.spec.whatwg.org/#file-state + */ + FILE, + + /** + * @see https://url.spec.whatwg.org/#file-host-state + */ + FILE_HOST, + + /** + * @see https://url.spec.whatwg.org/#file-slash-state + */ + FILE_SLASH, + + /** + * @see https://url.spec.whatwg.org/#path-or-authority-state + */ + PATH_OR_AUTHORITY, + + /** + * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state + */ + SPECIAL_AUTHORITY_IGNORE_SLASHES, + + /** + * @see https://url.spec.whatwg.org/#special-authority-slashes-state + */ + SPECIAL_AUTHORITY_SLASHES, + + /** + * @see https://url.spec.whatwg.org/#special-relative-or-authority-state + */ + SPECIAL_RELATIVE_OR_AUTHORITY, + + /** + * @see https://url.spec.whatwg.org/#query-state + */ + QUERY, + + /** + * @see https://url.spec.whatwg.org/#path-state + */ + PATH, + + /** + * @see https://url.spec.whatwg.org/#path-start-state + */ + PATH_START, + + /** + * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state + */ + OPAQUE_PATH, + + /** + * @see https://url.spec.whatwg.org/#port-state + */ + PORT, +}; + +/** + * Stringify a URL state machine state. + */ +ada_warn_unused std::string to_string(ada::state s); + +} // namespace ada + +#endif // ADA_STATE_H +/* end file include/ada/state.h */ +/* begin file include/ada/unicode.h */ +/** + * @file unicode.h + * @brief Definitions for all unicode specific functions. + */ +#ifndef ADA_UNICODE_H +#define ADA_UNICODE_H + + +#include +#include + +/** + * Unicode operations. These functions are not part of our public API and may + * change at any time. + * + * @private + * @namespace ada::unicode + * @brief Includes the definitions for unicode operations + */ +namespace ada::unicode { + +/** + * @private + * We receive a UTF-8 string representing a domain name. + * If the string is percent encoded, we apply percent decoding. + * + * Given a domain, we need to identify its labels. + * They are separated by label-separators: + * + * U+002E (.) FULL STOP + * U+FF0E FULLWIDTH FULL STOP + * U+3002 IDEOGRAPHIC FULL STOP + * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP + * + * They are all mapped to U+002E. + * + * We process each label into a string that should not exceed 63 octets. + * If the string is already punycode (starts with "xn--"), then we must + * scan it to look for unallowed code points. + * Otherwise, if the string is not pure ASCII, we need to transcode it + * to punycode by following RFC 3454 which requires us to + * - Map characters (see section 3), + * - Normalize (see section 4), + * - Reject forbidden characters, + * - Check for right-to-left characters and if so, check all requirements (see + * section 6), + * - Optionally reject based on unassigned code points (section 7). + * + * The Unicode standard provides a table of code points with a mapping, a list + * of forbidden code points and so forth. This table is subject to change and + * will vary based on the implementation. For Unicode 15, the table is at + * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt + * If you use ICU, they parse this table and map it to code using a Python + * script. + * + * The resulting strings should not exceed 255 octets according to RFC 1035 + * section 2.3.4. ICU checks for label size and domain size, but these errors + * are ignored. + * + * @see https://url.spec.whatwg.org/#concept-domain-to-ascii + * + */ +bool to_ascii(std::optional& out, std::string_view plain, + size_t first_percent); + +/** + * @private + * Checks if the input has tab or newline characters. + * + * @attention The has_tabs_or_newline function is a bottleneck and it is simple + * enough that compilers like GCC can 'autovectorize it'. + */ +ada_really_inline bool has_tabs_or_newline( + std::string_view user_input) noexcept; + +/** + * @private + * Checks if the input is a forbidden host code point. + * @see https://url.spec.whatwg.org/#forbidden-host-code-point + */ +ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept; + +/** + * @private + * Checks if the input contains a forbidden domain code point. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr bool contains_forbidden_domain_code_point( + const char* input, size_t length) noexcept; + +/** + * @private + * Checks if the input contains a forbidden domain code point in which case + * the first bit is set to 1. If the input contains an upper case ASCII letter, + * then the second bit is set to 1. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr uint8_t +contains_forbidden_domain_code_point_or_upper(const char* input, + size_t length) noexcept; + +/** + * @private + * Checks if the input is a forbidden domain code point. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr bool is_forbidden_domain_code_point( + char c) noexcept; + +/** + * @private + * Checks if the input is alphanumeric, '+', '-' or '.' + */ +ada_really_inline constexpr bool is_alnum_plus(char c) noexcept; + +/** + * @private + * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex + * digit. An ASCII upper hex digit is an ASCII digit or a code point in the + * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an + * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive. + */ +ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept; + +/** + * @private + * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9), + * inclusive. + */ +ada_really_inline constexpr bool is_ascii_digit(char c) noexcept; + +/** + * @private + * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII + * character. + */ +ada_really_inline constexpr bool is_ascii(char32_t c) noexcept; + +/** + * @private + * Checks if the input is a C0 control or space character. + * + * @details A C0 control or space is a C0 control or U+0020 SPACE. + * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION + * SEPARATOR ONE, inclusive. + */ +ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept; + +/** + * @private + * Checks if the input is a ASCII tab or newline character. + * + * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR. + */ +ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept; + +/** + * @private + * @details A double-dot path segment must be ".." or an ASCII case-insensitive + * match for ".%2e", "%2e.", or "%2e%2e". + */ +ada_really_inline constexpr bool is_double_dot_path_segment( + std::string_view input) noexcept; + +/** + * @private + * @details A single-dot path segment must be "." or an ASCII case-insensitive + * match for "%2e". + */ +ada_really_inline constexpr bool is_single_dot_path_segment( + std::string_view input) noexcept; + +/** + * @private + * @details ipv4 character might contain 0-9 or a-f character ranges. + */ +ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept; + +/** + * @private + * @details Convert hex to binary. Caller is responsible to ensure that + * the parameter is an hexadecimal digit (0-9, A-F, a-f). + */ +ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept; + +/** + * @private + * first_percent should be = input.find('%') + * + * @todo It would be faster as noexcept maybe, but it could be unsafe since. + * @author Node.js + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245 + * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom + */ +std::string percent_decode(std::string_view input, size_t first_percent); + +/** + * @private + * Returns a percent-encoding string whether percent encoding was needed or not. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +std::string percent_encode(std::string_view input, + const uint8_t character_set[]); +/** + * @private + * Returns a percent-encoded string version of input, while starting the percent + * encoding at the provided index. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +std::string percent_encode(std::string_view input, + const uint8_t character_set[], size_t index); +/** + * @private + * Returns true if percent encoding was needed, in which case, we store + * the percent-encoded content in 'out'. If the boolean 'append' is set to + * true, the content is appended to 'out'. + * If percent encoding is not needed, out is left unchanged. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +template +bool percent_encode(std::string_view input, const uint8_t character_set[], + std::string& out); +/** + * @private + * Returns the index at which percent encoding should start, or (equivalently), + * the length of the prefix that does not require percent encoding. + */ +ada_really_inline size_t percent_encode_index(std::string_view input, + const uint8_t character_set[]); +/** + * @private + * Lowers the string in-place, assuming that the content is ASCII. + * Return true if the content was ASCII. + */ +constexpr bool to_lower_ascii(char* input, size_t length) noexcept; +} // namespace ada::unicode + +#endif // ADA_UNICODE_H +/* end file include/ada/unicode.h */ +/* begin file include/ada/url_base-inl.h */ +/** + * @file url_base-inl.h + * @brief Inline functions for url base + */ +#ifndef ADA_URL_BASE_INL_H +#define ADA_URL_BASE_INL_H + + +#include +#if ADA_REGULAR_VISUAL_STUDIO +#include +#endif // ADA_REGULAR_VISUAL_STUDIO + +namespace ada { + +[[nodiscard]] ada_really_inline constexpr bool url_base::is_special() + const noexcept { + return type != ada::scheme::NOT_SPECIAL; +} + +[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept { + return ada::scheme::get_special_port(type); +} + +[[nodiscard]] ada_really_inline uint16_t +url_base::scheme_default_port() const noexcept { + return scheme::get_special_port(type); +} + +} // namespace ada + +#endif // ADA_URL_BASE_INL_H +/* end file include/ada/url_base-inl.h */ +/* begin file include/ada/url-inl.h */ +/** + * @file url-inl.h + * @brief Definitions for the URL + */ +#ifndef ADA_URL_INL_H +#define ADA_URL_INL_H + + +#include +#include +#include +#if ADA_REGULAR_VISUAL_STUDIO +#include +#endif // ADA_REGULAR_VISUAL_STUDIO + +namespace ada { +[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept { + return !username.empty() || !password.empty(); +} +[[nodiscard]] ada_really_inline bool url::has_port() const noexcept { + return port.has_value(); +} +[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const { + return !host.has_value() || host.value().empty() || + type == ada::scheme::type::FILE; +} +[[nodiscard]] inline bool url::has_empty_hostname() const noexcept { + if (!host.has_value()) { + return false; + } + return host.value().empty(); +} +[[nodiscard]] inline bool url::has_hostname() const noexcept { + return host.has_value(); +} +inline std::ostream &operator<<(std::ostream &out, const ada::url &u) { + return out << u.to_string(); +} + +[[nodiscard]] size_t url::get_pathname_length() const noexcept { + return path.size(); +} + +[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept { + return path; +} + +[[nodiscard]] ada_really_inline ada::url_components url::get_components() + const noexcept { + url_components out{}; + + // protocol ends with ':'. for example: "https:" + out.protocol_end = uint32_t(get_protocol().size()); + + // Trailing index is always the next character of the current one. + size_t running_index = out.protocol_end; + + if (host.has_value()) { + // 2 characters for "//" and 1 character for starting index + out.host_start = out.protocol_end + 2; + + if (has_credentials()) { + out.username_end = uint32_t(out.host_start + username.size()); + + out.host_start += uint32_t(username.size()); + + if (!password.empty()) { + out.host_start += uint32_t(password.size() + 1); + } + + out.host_end = uint32_t(out.host_start + host.value().size()); + } else { + out.username_end = out.host_start; + + // Host does not start with "@" if it does not include credentials. + out.host_end = uint32_t(out.host_start + host.value().size()) - 1; + } + + running_index = out.host_end + 1; + } else { + // Update host start and end date to the same index, since it does not + // exist. + out.host_start = out.protocol_end; + out.host_end = out.host_start; + + if (!has_opaque_path && path.starts_with("//")) { + // If url's host is null, url does not have an opaque path, url's path's + // size is greater than 1, and url's path[0] is the empty string, then + // append U+002F (/) followed by U+002E (.) to output. + running_index = out.protocol_end + 2; + } else { + running_index = out.protocol_end; + } + } + + if (port.has_value()) { + out.port = *port; + running_index += helpers::fast_digit_count(*port) + 1; // Port omits ':' + } + + out.pathname_start = uint32_t(running_index); + + running_index += path.size(); + + if (query.has_value()) { + out.search_start = uint32_t(running_index); + running_index += get_search().size(); + if (get_search().empty()) { + running_index++; + } + } + + if (hash.has_value()) { + out.hash_start = uint32_t(running_index); + } + + return out; +} + +inline void url::update_base_hostname(std::string_view input) { host = input; } + +inline void url::update_unencoded_base_hash(std::string_view input) { + // We do the percent encoding + hash = unicode::percent_encode(input, + ada::character_sets::FRAGMENT_PERCENT_ENCODE); +} + +inline void url::update_base_search(std::string_view input, + const uint8_t query_percent_encode_set[]) { + query = ada::unicode::percent_encode(input, query_percent_encode_set); +} + +inline void url::update_base_search(std::optional &&input) { + query = std::move(input); +} + +inline void url::update_base_pathname(const std::string_view input) { + path = input; +} + +inline void url::update_base_username(const std::string_view input) { + username = input; +} + +inline void url::update_base_password(const std::string_view input) { + password = input; +} + +inline void url::update_base_port(std::optional input) { + port = input; +} + +constexpr void url::clear_pathname() { path.clear(); } + +constexpr void url::clear_search() { query = std::nullopt; } + +[[nodiscard]] constexpr bool url::has_hash() const noexcept { + return hash.has_value(); +} + +[[nodiscard]] constexpr bool url::has_search() const noexcept { + return query.has_value(); +} + +constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; } + +inline void url::set_scheme(std::string &&new_scheme) noexcept { + type = ada::scheme::get_scheme_type(new_scheme); + // We only move the 'scheme' if it is non-special. + if (!is_special()) { + non_special_scheme = std::move(new_scheme); + } +} + +constexpr void url::copy_scheme(ada::url &&u) noexcept { + non_special_scheme = u.non_special_scheme; + type = u.type; +} + +constexpr void url::copy_scheme(const ada::url &u) { + non_special_scheme = u.non_special_scheme; + type = u.type; +} + +[[nodiscard]] ada_really_inline std::string url::get_href() const noexcept { + std::string output = get_protocol(); + + if (host.has_value()) { + output += "//"; + if (has_credentials()) { + output += username; + if (!password.empty()) { + output += ":" + get_password(); + } + output += "@"; + } + output += host.value(); + if (port.has_value()) { + output += ":" + get_port(); + } + } else if (!has_opaque_path && path.starts_with("//")) { + // If url's host is null, url does not have an opaque path, url's path's + // size is greater than 1, and url's path[0] is the empty string, then + // append U+002F (/) followed by U+002E (.) to output. + output += "/."; + } + output += path; + if (query.has_value()) { + output += "?" + query.value(); + } + if (hash.has_value()) { + output += "#" + hash.value(); + } + return output; +} + +ada_really_inline size_t url::parse_port(std::string_view view, + bool check_trailing_content) noexcept { + ada_log("parse_port('", view, "') ", view.size()); + if (!view.empty() && view[0] == '-') { + ada_log("parse_port: view[0] == '0' && view.size() > 1"); + is_valid = false; + return 0; + } + uint16_t parsed_port{}; + auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port); + if (r.ec == std::errc::result_out_of_range) { + ada_log("parse_port: r.ec == std::errc::result_out_of_range"); + is_valid = false; + return 0; + } + ada_log("parse_port: ", parsed_port); + const auto consumed = size_t(r.ptr - view.data()); + ada_log("parse_port: consumed ", consumed); + if (check_trailing_content) { + is_valid &= + (consumed == view.size() || view[consumed] == '/' || + view[consumed] == '?' || (is_special() && view[consumed] == '\\')); + } + ada_log("parse_port: is_valid = ", is_valid); + if (is_valid) { + // scheme_default_port can return 0, and we should allow 0 as a base port. + auto default_port = scheme_default_port(); + bool is_port_valid = (default_port == 0 && parsed_port == 0) || + (default_port != parsed_port); + port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port) + : std::nullopt; + } + return consumed; +} + +} // namespace ada + +#endif // ADA_URL_H +/* end file include/ada/url-inl.h */ +/* begin file include/ada/url_components-inl.h */ +/** + * @file url_components.h + * @brief Declaration for the URL Components + */ +#ifndef ADA_URL_COMPONENTS_INL_H +#define ADA_URL_COMPONENTS_INL_H + + +namespace ada { + +[[nodiscard]] constexpr bool url_components::check_offset_consistency() + const noexcept { + /** + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + */ + // These conditions can be made more strict. + if (protocol_end == url_components::omitted) { + return false; + } + uint32_t index = protocol_end; + + if (username_end == url_components::omitted) { + return false; + } + if (username_end < index) { + return false; + } + index = username_end; + + if (host_start == url_components::omitted) { + return false; + } + if (host_start < index) { + return false; + } + index = host_start; + + if (port != url_components::omitted) { + if (port > 0xffff) { + return false; + } + uint32_t port_length = helpers::fast_digit_count(port) + 1; + if (index + port_length < index) { + return false; + } + index += port_length; + } + + if (pathname_start == url_components::omitted) { + return false; + } + if (pathname_start < index) { + return false; + } + index = pathname_start; + + if (search_start != url_components::omitted) { + if (search_start < index) { + return false; + } + index = search_start; + } + + if (hash_start != url_components::omitted) { + if (hash_start < index) { + return false; + } + } + + return true; +} + +} // namespace ada +#endif +/* end file include/ada/url_components-inl.h */ +/* begin file include/ada/url_aggregator.h */ +/** + * @file url_aggregator.h + * @brief Declaration for the basic URL definitions + */ +#ifndef ADA_URL_AGGREGATOR_H +#define ADA_URL_AGGREGATOR_H + +#include +#include +#include + + +namespace ada { + +namespace parser {} + +/** + * @brief Lightweight URL struct. + * + * @details The url_aggregator class aims to minimize temporary memory + * allocation while representing a parsed URL. Internally, it contains a single + * normalized URL (the href), and it makes available the components, mostly + * using std::string_view. + */ +struct url_aggregator : url_base { + url_aggregator() = default; + url_aggregator(const url_aggregator &u) = default; + url_aggregator(url_aggregator &&u) noexcept = default; + url_aggregator &operator=(url_aggregator &&u) noexcept = default; + url_aggregator &operator=(const url_aggregator &u) = default; + ~url_aggregator() override = default; + + bool set_href(std::string_view input); + bool set_host(std::string_view input); + bool set_hostname(std::string_view input); + bool set_protocol(std::string_view input); + bool set_username(std::string_view input); + bool set_password(std::string_view input); + bool set_port(std::string_view input); + bool set_pathname(std::string_view input); + void set_search(std::string_view input); + void set_hash(std::string_view input); + + [[nodiscard]] bool has_valid_domain() const noexcept override; + /** + * The origin getter steps are to return the serialization of this's URL's + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin + */ + [[nodiscard]] std::string get_origin() const noexcept override; + /** + * Return the normalized string. + * This function does not allocate memory. + * It is highly efficient. + * @return a constant reference to the underlying normalized URL. + * @see https://url.spec.whatwg.org/#dom-url-href + * @see https://url.spec.whatwg.org/#concept-url-serializer + */ + [[nodiscard]] constexpr std::string_view get_href() const noexcept + ada_lifetime_bound; + /** + * The username getter steps are to return this's URL's username. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-username + */ + [[nodiscard]] std::string_view get_username() const noexcept + ada_lifetime_bound; + /** + * The password getter steps are to return this's URL's password. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-password + */ + [[nodiscard]] std::string_view get_password() const noexcept + ada_lifetime_bound; + /** + * Return this's URL's port, serialized. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + [[nodiscard]] std::string_view get_port() const noexcept ada_lifetime_bound; + /** + * Return U+0023 (#), followed by this's URL's fragment. + * This function does not allocate memory. + * @return a lightweight std::string_view.. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + [[nodiscard]] std::string_view get_hash() const noexcept ada_lifetime_bound; + /** + * Return url's host, serialized, followed by U+003A (:) and url's port, + * serialized. + * This function does not allocate memory. + * When there is no host, this function returns the empty view. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-host + */ + [[nodiscard]] std::string_view get_host() const noexcept ada_lifetime_bound; + /** + * Return this's URL's host, serialized. + * This function does not allocate memory. + * When there is no host, this function returns the empty view. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-hostname + */ + [[nodiscard]] std::string_view get_hostname() const noexcept + ada_lifetime_bound; + /** + * The pathname getter steps are to return the result of URL path serializing + * this's URL. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-pathname + */ + [[nodiscard]] constexpr std::string_view get_pathname() const noexcept + ada_lifetime_bound; + /** + * Compute the pathname length in bytes without instantiating a view or a + * string. + * @return size of the pathname in bytes + * @see https://url.spec.whatwg.org/#dom-url-pathname + */ + [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept; + /** + * Return U+003F (?), followed by this's URL's query. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + [[nodiscard]] std::string_view get_search() const noexcept ada_lifetime_bound; + /** + * The protocol getter steps are to return this's URL's scheme, followed by + * U+003A (:). + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-protocol + */ + [[nodiscard]] std::string_view get_protocol() const noexcept + ada_lifetime_bound; + + /** + * A URL includes credentials if its username or password is not the empty + * string. + */ + [[nodiscard]] ada_really_inline constexpr bool has_credentials() + const noexcept; + + /** + * Useful for implementing efficient serialization for the URL. + * + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + * + * Inspired after servo/url + * + * @return a constant reference to the underlying component attribute. + * + * @see + * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 + */ + [[nodiscard]] ada_really_inline const url_components &get_components() + const noexcept; + /** + * Returns a string representation of this URL. + */ + [[nodiscard]] std::string to_string() const override; + /** + * Returns a string diagram of this URL. + */ + [[nodiscard]] std::string to_diagram() const; + + /** + * Verifies that the parsed URL could be valid. Useful for debugging purposes. + * @return true if the URL is valid, otherwise return true of the offsets are + * possible. + */ + [[nodiscard]] constexpr bool validate() const noexcept; + + /** @return true if it has an host but it is the empty string */ + [[nodiscard]] constexpr bool has_empty_hostname() const noexcept; + /** @return true if it has a host (included an empty host) */ + [[nodiscard]] constexpr bool has_hostname() const noexcept; + /** @return true if the URL has a non-empty username */ + [[nodiscard]] constexpr bool has_non_empty_username() const noexcept; + /** @return true if the URL has a non-empty password */ + [[nodiscard]] constexpr bool has_non_empty_password() const noexcept; + /** @return true if the URL has a (non default) port */ + [[nodiscard]] constexpr bool has_port() const noexcept; + /** @return true if the URL has a password */ + [[nodiscard]] constexpr bool has_password() const noexcept; + /** @return true if the URL has a hash component */ + [[nodiscard]] constexpr bool has_hash() const noexcept override; + /** @return true if the URL has a search component */ + [[nodiscard]] constexpr bool has_search() const noexcept override; + + inline void clear_port(); + inline void clear_hash(); + inline void clear_search() override; -inline void url::update_base_search(std::string_view input, - const uint8_t query_percent_encode_set[]) { - query = ada::unicode::percent_encode(input, query_percent_encode_set); -} + private: + // helper methods + friend void helpers::strip_trailing_spaces_from_opaque_path( + url_aggregator &url) noexcept; + // parse_url methods + friend url_aggregator parser::parse_url( + std::string_view, const url_aggregator *); + + friend url_aggregator parser::parse_url_impl( + std::string_view, const url_aggregator *); + friend url_aggregator parser::parse_url_impl( + std::string_view, const url_aggregator *); + // url_pattern methods + template + friend tl::expected, errors> + parse_url_pattern_impl(std::variant input, + const std::string_view *base_url, + const url_pattern_options *options); -inline void url::update_base_search(std::optional input) { - query = input; -} + std::string buffer{}; + url_components components{}; -inline void url::update_base_pathname(const std::string_view input) { - path = input; -} + /** + * Returns true if neither the search, nor the hash nor the pathname + * have been set. + * @return true if the buffer is ready to receive the path. + */ + [[nodiscard]] ada_really_inline bool is_at_path() const noexcept; -inline void url::update_base_username(const std::string_view input) { - username = input; -} + inline void add_authority_slashes_if_needed() noexcept; -inline void url::update_base_password(const std::string_view input) { - password = input; -} + /** + * To optimize performance, you may indicate how much memory to allocate + * within this instance. + */ + constexpr void reserve(uint32_t capacity); -inline void url::update_base_port(std::optional input) { - port = input; -} + ada_really_inline size_t parse_port( + std::string_view view, bool check_trailing_content) noexcept override; -inline void url::clear_pathname() { path.clear(); } + ada_really_inline size_t parse_port(std::string_view view) noexcept override { + return this->parse_port(view, false); + } -inline void url::clear_search() { query = std::nullopt; } + /** + * Return true on success. The 'in_place' parameter indicates whether the + * the string_view input is pointing in the buffer. When in_place is false, + * we must nearly always update the buffer. + * @see https://url.spec.whatwg.org/#concept-ipv4-parser + */ + [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place); -[[nodiscard]] inline bool url::has_hash() const noexcept { - return hash.has_value(); -} + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-ipv6-parser + */ + [[nodiscard]] bool parse_ipv6(std::string_view input); -[[nodiscard]] inline bool url::has_search() const noexcept { - return query.has_value(); -} + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-opaque-host-parser + */ + [[nodiscard]] bool parse_opaque_host(std::string_view input); -inline void url::set_protocol_as_file() { type = ada::scheme::type::FILE; } + ada_really_inline void parse_path(std::string_view input); -inline void url::set_scheme(std::string &&new_scheme) noexcept { - type = ada::scheme::get_scheme_type(new_scheme); - // We only move the 'scheme' if it is non-special. - if (!is_special()) { - non_special_scheme = std::move(new_scheme); - } -} + /** + * A URL cannot have a username/password/port if its host is null or the empty + * string, or its scheme is "file". + */ + [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const; -inline void url::copy_scheme(ada::url &&u) noexcept { - non_special_scheme = u.non_special_scheme; - type = u.type; -} + template + bool set_host_or_hostname(std::string_view input); -inline void url::copy_scheme(const ada::url &u) { - non_special_scheme = u.non_special_scheme; - type = u.type; -} + ada_really_inline bool parse_host(std::string_view input); -[[nodiscard]] ada_really_inline std::string url::get_href() const noexcept { - std::string output = get_protocol(); + inline void update_base_authority(std::string_view base_buffer, + const url_components &base); + inline void update_unencoded_base_hash(std::string_view input); + inline void update_base_hostname(std::string_view input); + inline void update_base_search(std::string_view input); + inline void update_base_search(std::string_view input, + const uint8_t *query_percent_encode_set); + inline void update_base_pathname(std::string_view input); + inline void update_base_username(std::string_view input); + inline void append_base_username(std::string_view input); + inline void update_base_password(std::string_view input); + inline void append_base_password(std::string_view input); + inline void update_base_port(uint32_t input); + inline void append_base_pathname(std::string_view input); + [[nodiscard]] inline uint32_t retrieve_base_port() const; + constexpr void clear_hostname(); + constexpr void clear_password(); + constexpr void clear_pathname() override; + [[nodiscard]] constexpr bool has_dash_dot() const noexcept; + void delete_dash_dot(); + inline void consume_prepared_path(std::string_view input); + template + [[nodiscard]] ada_really_inline bool parse_scheme_with_colon( + std::string_view input); + ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end, + std::string_view input); + [[nodiscard]] constexpr bool has_authority() const noexcept; + constexpr void set_protocol_as_file(); + inline void set_scheme(std::string_view new_scheme) noexcept; + /** + * Fast function to set the scheme from a view with a colon in the + * buffer, does not change type. + */ + inline void set_scheme_from_view_with_colon( + std::string_view new_scheme_with_colon) noexcept; + inline void copy_scheme(const url_aggregator &u) noexcept; - if (host.has_value()) { - output += "//"; - if (has_credentials()) { - output += username; - if (!password.empty()) { - output += ":" + get_password(); - } - output += "@"; - } - output += host.value(); - if (port.has_value()) { - output += ":" + get_port(); - } - } else if (!has_opaque_path && checkers::begins_with(path, "//")) { - // If url's host is null, url does not have an opaque path, url's path's - // size is greater than 1, and url's path[0] is the empty string, then - // append U+002F (/) followed by U+002E (.) to output. - output += "/."; - } - output += path; - if (query.has_value()) { - output += "?" + query.value(); - } - if (hash.has_value()) { - output += "#" + hash.value(); - } - return output; -} + inline void update_host_to_base_host(const std::string_view input) noexcept; -ada_really_inline size_t url::parse_port(std::string_view view, - bool check_trailing_content) noexcept { - ada_log("parse_port('", view, "') ", view.size()); - if (!view.empty() && view[0] == '-') { - ada_log("parse_port: view[0] == '0' && view.size() > 1"); - is_valid = false; - return 0; - } - uint16_t parsed_port{}; - auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port); - if (r.ec == std::errc::result_out_of_range) { - ada_log("parse_port: r.ec == std::errc::result_out_of_range"); - is_valid = false; - return 0; - } - ada_log("parse_port: ", parsed_port); - const size_t consumed = size_t(r.ptr - view.data()); - ada_log("parse_port: consumed ", consumed); - if (check_trailing_content) { - is_valid &= - (consumed == view.size() || view[consumed] == '/' || - view[consumed] == '?' || (is_special() && view[consumed] == '\\')); - } - ada_log("parse_port: is_valid = ", is_valid); - if (is_valid) { - // scheme_default_port can return 0, and we should allow 0 as a base port. - auto default_port = scheme_default_port(); - bool is_port_valid = (default_port == 0 && parsed_port == 0) || - (default_port != parsed_port); - port = (r.ec == std::errc() && is_port_valid) - ? std::optional(parsed_port) - : std::nullopt; - } - return consumed; -} +}; // url_aggregator +inline std::ostream &operator<<(std::ostream &out, const url &u); } // namespace ada -#endif // ADA_URL_H -/* end file include/ada/url-inl.h */ +#endif +/* end file include/ada/url_aggregator.h */ /* begin file include/ada/url_aggregator-inl.h */ /** * @file url_aggregator-inl.h @@ -5947,7 +7091,6 @@ ada_really_inline size_t url::parse_port(std::string_view view, */ #ifndef ADA_UNICODE_INL_H #define ADA_UNICODE_INL_H -#include /** * Unicode operations. These functions are not part of our public API and may @@ -5960,18 +7103,39 @@ ada_really_inline size_t url::parse_port(std::string_view view, namespace ada::unicode { ada_really_inline size_t percent_encode_index(const std::string_view input, const uint8_t character_set[]) { - return std::distance( - input.begin(), - std::find_if(input.begin(), input.end(), [character_set](const char c) { - return character_sets::bit_at(character_set, c); - })); + const char* data = input.data(); + const size_t size = input.size(); + + // Process 8 bytes at a time using unrolled loop + size_t i = 0; + for (; i + 8 <= size; i += 8) { + unsigned char chunk[8]; + std::memcpy(&chunk, data + i, + 8); // entices compiler to unconditionally process 8 characters + + // Check 8 characters at once + for (size_t j = 0; j < 8; j++) { + if (character_sets::bit_at(character_set, chunk[j])) { + return i + j; + } + } + } + + // Handle remaining bytes + for (; i < size; i++) { + if (character_sets::bit_at(character_set, data[i])) { + return i; + } + } + + return size; } } // namespace ada::unicode #endif // ADA_UNICODE_INL_H /* end file include/ada/unicode-inl.h */ -#include +#include #include namespace ada { @@ -5982,7 +7146,7 @@ inline void url_aggregator::update_base_authority( base.protocol_end, base.host_start - base.protocol_end); ada_log("url_aggregator::update_base_authority ", input); - bool input_starts_with_dash = checkers::begins_with(input, "//"); + bool input_starts_with_dash = input.starts_with("//"); uint32_t diff = components.host_start - components.protocol_end; buffer.erase(components.protocol_end, @@ -6222,9 +7386,8 @@ inline void url_aggregator::update_base_pathname(const std::string_view input) { ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer)); ADA_ASSERT_TRUE(validate()); - const bool begins_with_dashdash = checkers::begins_with(input, "//"); + const bool begins_with_dashdash = input.starts_with("//"); if (!begins_with_dashdash && has_dash_dot()) { - ada_log("url_aggregator::update_base_pathname has /.: \n", to_diagram()); // We must delete the ./ delete_dash_dot(); } @@ -6247,8 +7410,6 @@ inline void url_aggregator::update_base_pathname(const std::string_view input) { if (components.hash_start != url_components::omitted) { components.hash_start += difference; } - ada_log("url_aggregator::update_base_pathname end '", input, "' [", - input.size(), " bytes] \n", to_diagram()); ADA_ASSERT_TRUE(validate()); } @@ -6367,8 +7528,8 @@ inline void url_aggregator::append_base_username(const std::string_view input) { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_password() { - ada_log("url_aggregator::clear_password ", to_string(), "\n", to_diagram()); +constexpr void url_aggregator::clear_password() { + ada_log("url_aggregator::clear_password ", to_string()); ADA_ASSERT_TRUE(validate()); if (!has_password()) { return; @@ -6589,7 +7750,7 @@ inline void url_aggregator::clear_hash() { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_pathname() { +constexpr void url_aggregator::clear_pathname() { ada_log("url_aggregator::clear_pathname"); ADA_ASSERT_TRUE(validate()); uint32_t ending_index = uint32_t(buffer.size()); @@ -6624,7 +7785,7 @@ inline void url_aggregator::clear_pathname() { ada_log("url_aggregator::clear_pathname completed, running checks... ok"); } -inline void url_aggregator::clear_hostname() { +constexpr void url_aggregator::clear_hostname() { ada_log("url_aggregator::clear_hostname"); ADA_ASSERT_TRUE(validate()); if (!has_authority()) { @@ -6661,22 +7822,22 @@ inline void url_aggregator::clear_hostname() { ADA_ASSERT_TRUE(validate()); } -[[nodiscard]] inline bool url_aggregator::has_hash() const noexcept { +[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept { ada_log("url_aggregator::has_hash"); return components.hash_start != url_components::omitted; } -[[nodiscard]] inline bool url_aggregator::has_search() const noexcept { +[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept { ada_log("url_aggregator::has_search"); return components.search_start != url_components::omitted; } -ada_really_inline bool url_aggregator::has_credentials() const noexcept { +constexpr bool url_aggregator::has_credentials() const noexcept { ada_log("url_aggregator::has_credentials"); return has_non_empty_username() || has_non_empty_password(); } -inline bool url_aggregator::cannot_have_credentials_or_port() const { +constexpr bool url_aggregator::cannot_have_credentials_or_port() const { ada_log("url_aggregator::cannot_have_credentials_or_port"); return type == ada::scheme::type::FILE || components.host_start == components.host_end; @@ -6687,7 +7848,8 @@ url_aggregator::get_components() const noexcept { return components; } -[[nodiscard]] inline bool ada::url_aggregator::has_authority() const noexcept { +[[nodiscard]] constexpr bool ada::url_aggregator::has_authority() + const noexcept { ada_log("url_aggregator::has_authority"); // Performance: instead of doing this potentially expensive check, we could // have a boolean in the struct. @@ -6722,28 +7884,28 @@ inline void ada::url_aggregator::add_authority_slashes_if_needed() noexcept { ADA_ASSERT_TRUE(validate()); } -inline void ada::url_aggregator::reserve(uint32_t capacity) { +constexpr void ada::url_aggregator::reserve(uint32_t capacity) { buffer.reserve(capacity); } -inline bool url_aggregator::has_non_empty_username() const noexcept { +constexpr bool url_aggregator::has_non_empty_username() const noexcept { ada_log("url_aggregator::has_non_empty_username"); return components.protocol_end + 2 < components.username_end; } -inline bool url_aggregator::has_non_empty_password() const noexcept { +constexpr bool url_aggregator::has_non_empty_password() const noexcept { ada_log("url_aggregator::has_non_empty_password"); return components.host_start - components.username_end > 0; } -inline bool url_aggregator::has_password() const noexcept { +constexpr bool url_aggregator::has_password() const noexcept { ada_log("url_aggregator::has_password"); // This function does not care about the length of the password return components.host_start > components.username_end && buffer[components.username_end] == ':'; } -inline bool url_aggregator::has_empty_hostname() const noexcept { +constexpr bool url_aggregator::has_empty_hostname() const noexcept { if (!has_hostname()) { return false; } @@ -6756,18 +7918,18 @@ inline bool url_aggregator::has_empty_hostname() const noexcept { return components.username_end != components.host_start; } -inline bool url_aggregator::has_hostname() const noexcept { +constexpr bool url_aggregator::has_hostname() const noexcept { return has_authority(); } -inline bool url_aggregator::has_port() const noexcept { +constexpr bool url_aggregator::has_port() const noexcept { ada_log("url_aggregator::has_port"); // A URL cannot have a username/password/port if its host is null or the empty // string, or its scheme is "file". return has_hostname() && components.pathname_start != components.host_end; } -[[nodiscard]] inline bool url_aggregator::has_dash_dot() const noexcept { +[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept { // If url's host is null, url does not have an opaque path, url's path's size // is greater than 1, and url's path[0] is the empty string, then append // U+002F (/) followed by U+002E (.) to output. @@ -6799,8 +7961,8 @@ inline bool url_aggregator::has_port() const noexcept { buffer[components.host_end + 1] == '.'; } -[[nodiscard]] inline std::string_view url_aggregator::get_href() const noexcept - ada_lifetime_bound { +[[nodiscard]] constexpr std::string_view url_aggregator::get_href() + const noexcept ada_lifetime_bound { ada_log("url_aggregator::get_href"); return buffer; } @@ -6844,7 +8006,7 @@ ada_really_inline size_t url_aggregator::parse_port( return consumed; } -inline void url_aggregator::set_protocol_as_file() { +constexpr void url_aggregator::set_protocol_as_file() { ada_log("url_aggregator::set_protocol_as_file "); ADA_ASSERT_TRUE(validate()); type = ada::scheme::type::FILE; @@ -6871,13 +8033,220 @@ inline void url_aggregator::set_protocol_as_file() { if (components.hash_start != url_components::omitted) { components.hash_start += new_difference; } - ADA_ASSERT_TRUE(validate()); + ADA_ASSERT_TRUE(validate()); +} + +[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept { + if (!is_valid) { + return true; + } + if (!components.check_offset_consistency()) { + ada_log("url_aggregator::validate inconsistent components \n", + to_diagram()); + return false; + } + // We have a credible components struct, but let us investivate more + // carefully: + /** + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + */ + if (components.protocol_end == url_components::omitted) { + ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram()); + return false; + } + if (components.username_end == url_components::omitted) { + ada_log("url_aggregator::validate omitted username_end \n", to_diagram()); + return false; + } + if (components.host_start == url_components::omitted) { + ada_log("url_aggregator::validate omitted host_start \n", to_diagram()); + return false; + } + if (components.host_end == url_components::omitted) { + ada_log("url_aggregator::validate omitted host_end \n", to_diagram()); + return false; + } + if (components.pathname_start == url_components::omitted) { + ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram()); + return false; + } + + if (components.protocol_end > buffer.size()) { + ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram()); + return false; + } + if (components.username_end > buffer.size()) { + ada_log("url_aggregator::validate username_end overflow \n", to_diagram()); + return false; + } + if (components.host_start > buffer.size()) { + ada_log("url_aggregator::validate host_start overflow \n", to_diagram()); + return false; + } + if (components.host_end > buffer.size()) { + ada_log("url_aggregator::validate host_end overflow \n", to_diagram()); + return false; + } + if (components.pathname_start > buffer.size()) { + ada_log("url_aggregator::validate pathname_start overflow \n", + to_diagram()); + return false; + } + + if (components.protocol_end > 0) { + if (buffer[components.protocol_end - 1] != ':') { + ada_log( + "url_aggregator::validate missing : at the end of the protocol \n", + to_diagram()); + return false; + } + } + + if (components.username_end != buffer.size() && + components.username_end > components.protocol_end + 2) { + if (buffer[components.username_end] != ':' && + buffer[components.username_end] != '@') { + ada_log( + "url_aggregator::validate missing : or @ at the end of the username " + "\n", + to_diagram()); + return false; + } + } + + if (components.host_start != buffer.size()) { + if (components.host_start > components.username_end) { + if (buffer[components.host_start] != '@') { + ada_log( + "url_aggregator::validate missing @ at the end of the password \n", + to_diagram()); + return false; + } + } else if (components.host_start == components.username_end && + components.host_end > components.host_start) { + if (components.host_start == components.protocol_end + 2) { + if (buffer[components.protocol_end] != '/' || + buffer[components.protocol_end + 1] != '/') { + ada_log( + "url_aggregator::validate missing // between protocol and host " + "\n", + to_diagram()); + return false; + } + } else { + if (components.host_start > components.protocol_end && + buffer[components.host_start] != '@') { + ada_log( + "url_aggregator::validate missing @ at the end of the username " + "\n", + to_diagram()); + return false; + } + } + } else { + if (components.host_end != components.host_start) { + ada_log("url_aggregator::validate expected omitted host \n", + to_diagram()); + return false; + } + } + } + if (components.host_end != buffer.size() && + components.pathname_start > components.host_end) { + if (components.pathname_start == components.host_end + 2 && + buffer[components.host_end] == '/' && + buffer[components.host_end + 1] == '.') { + if (components.pathname_start + 1 >= buffer.size() || + buffer[components.pathname_start] != '/' || + buffer[components.pathname_start + 1] != '/') { + ada_log( + "url_aggregator::validate expected the path to begin with // \n", + to_diagram()); + return false; + } + } else if (buffer[components.host_end] != ':') { + ada_log("url_aggregator::validate missing : at the port \n", + to_diagram()); + return false; + } + } + if (components.pathname_start != buffer.size() && + components.pathname_start < components.search_start && + components.pathname_start < components.hash_start && !has_opaque_path) { + if (buffer[components.pathname_start] != '/') { + ada_log("url_aggregator::validate missing / at the path \n", + to_diagram()); + return false; + } + } + if (components.search_start != url_components::omitted) { + if (buffer[components.search_start] != '?') { + ada_log("url_aggregator::validate missing ? at the search \n", + to_diagram()); + return false; + } + } + if (components.hash_start != url_components::omitted) { + if (buffer[components.hash_start] != '#') { + ada_log("url_aggregator::validate missing # at the hash \n", + to_diagram()); + return false; + } + } + + return true; +} + +[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() + const noexcept ada_lifetime_bound { + ada_log("url_aggregator::get_pathname pathname_start = ", + components.pathname_start, " buffer.size() = ", buffer.size(), + " components.search_start = ", components.search_start, + " components.hash_start = ", components.hash_start); + auto ending_index = uint32_t(buffer.size()); + if (components.search_start != url_components::omitted) { + ending_index = components.search_start; + } else if (components.hash_start != url_components::omitted) { + ending_index = components.hash_start; + } + return helpers::substring(buffer, components.pathname_start, ending_index); } inline std::ostream &operator<<(std::ostream &out, const ada::url_aggregator &u) { return out << u.to_string(); } + +void url_aggregator::update_host_to_base_host( + const std::string_view input) noexcept { + ada_log("url_aggregator::update_host_to_base_host ", input); + ADA_ASSERT_TRUE(validate()); + ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer)); + if (type != ada::scheme::type::FILE) { + // Let host be the result of host parsing host_view with url is not special. + if (input.empty() && !is_special()) { + if (has_hostname()) { + clear_hostname(); + } else if (has_dash_dot()) { + add_authority_slashes_if_needed(); + delete_dash_dot(); + } + return; + } + } + update_base_hostname(input); + ADA_ASSERT_TRUE(validate()); + return; +} } // namespace ada #endif // ADA_URL_AGGREGATOR_INL_H @@ -6927,7 +8296,9 @@ struct url_search_params { * @see * https://github.com/web-platform-tests/wpt/blob/master/url/urlsearchparams-constructor.any.js */ - url_search_params(const std::string_view input) { initialize(input); } + explicit url_search_params(const std::string_view input) { + initialize(input); + } url_search_params(const url_search_params &u) = default; url_search_params(url_search_params &&u) noexcept = default; @@ -7057,7 +8428,7 @@ struct url_search_params_iter { */ inline std::optional next(); - inline bool has_next(); + inline bool has_next() const; private: static url_search_params EMPTY; @@ -7083,6 +8454,7 @@ struct url_search_params_iter { #include #include +#include #include #include #include @@ -7103,200 +8475,1760 @@ inline void url_search_params::initialize(std::string_view input) { input.remove_prefix(1); } - auto process_key_value = [&](const std::string_view current) { - auto equal = current.find('='); + auto process_key_value = [&](const std::string_view current) { + auto equal = current.find('='); + + if (equal == std::string_view::npos) { + std::string name(current); + std::ranges::replace(name, '+', ' '); + params.emplace_back(unicode::percent_decode(name, name.find('%')), ""); + } else { + std::string name(current.substr(0, equal)); + std::string value(current.substr(equal + 1)); + + std::ranges::replace(name, '+', ' '); + std::ranges::replace(value, '+', ' '); + + params.emplace_back(unicode::percent_decode(name, name.find('%')), + unicode::percent_decode(value, value.find('%'))); + } + }; + + while (!input.empty()) { + auto ampersand_index = input.find('&'); + + if (ampersand_index == std::string_view::npos) { + if (!input.empty()) { + process_key_value(input); + } + break; + } else if (ampersand_index != 0) { + process_key_value(input.substr(0, ampersand_index)); + } + + input.remove_prefix(ampersand_index + 1); + } +} + +inline void url_search_params::append(const std::string_view key, + const std::string_view value) { + params.emplace_back(key, value); +} + +inline size_t url_search_params::size() const noexcept { return params.size(); } + +inline std::optional url_search_params::get( + const std::string_view key) { + auto entry = std::ranges::find_if( + params, [&key](auto ¶m) { return param.first == key; }); + + if (entry == params.end()) { + return std::nullopt; + } + + return entry->second; +} + +inline std::vector url_search_params::get_all( + const std::string_view key) { + std::vector out{}; + + for (auto ¶m : params) { + if (param.first == key) { + out.emplace_back(param.second); + } + } + + return out; +} + +inline bool url_search_params::has(const std::string_view key) noexcept { + auto entry = std::ranges::find_if( + params, [&key](auto ¶m) { return param.first == key; }); + return entry != params.end(); +} + +inline bool url_search_params::has(std::string_view key, + std::string_view value) noexcept { + auto entry = std::ranges::find_if(params, [&key, &value](auto ¶m) { + return param.first == key && param.second == value; + }); + return entry != params.end(); +} + +inline std::string url_search_params::to_string() const { + auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE; + std::string out{}; + for (size_t i = 0; i < params.size(); i++) { + auto key = ada::unicode::percent_encode(params[i].first, character_set); + auto value = ada::unicode::percent_encode(params[i].second, character_set); + + // Performance optimization: Move this inside percent_encode. + std::ranges::replace(key, ' ', '+'); + std::ranges::replace(value, ' ', '+'); + + if (i != 0) { + out += "&"; + } + out.append(key); + out += "="; + out.append(value); + } + return out; +} + +inline void url_search_params::set(const std::string_view key, + const std::string_view value) { + const auto find = [&key](auto ¶m) { return param.first == key; }; + + auto it = std::ranges::find_if(params, find); + + if (it == params.end()) { + params.emplace_back(key, value); + } else { + it->second = value; + params.erase(std::remove_if(std::next(it), params.end(), find), + params.end()); + } +} + +inline void url_search_params::remove(const std::string_view key) { + std::erase_if(params, [&key](auto ¶m) { return param.first == key; }); +} + +inline void url_search_params::remove(const std::string_view key, + const std::string_view value) { + std::erase_if(params, [&key, &value](auto ¶m) { + return param.first == key && param.second == value; + }); +} + +inline void url_search_params::sort() { + std::ranges::stable_sort( + params, [](const key_value_pair &lhs, const key_value_pair &rhs) { + return lhs.first < rhs.first; + }); +} + +inline url_search_params_keys_iter url_search_params::get_keys() { + return url_search_params_keys_iter(*this); +} + +/** + * @see https://url.spec.whatwg.org/#interface-urlsearchparams + */ +inline url_search_params_values_iter url_search_params::get_values() { + return url_search_params_values_iter(*this); +} + +/** + * @see https://url.spec.whatwg.org/#interface-urlsearchparams + */ +inline url_search_params_entries_iter url_search_params::get_entries() { + return url_search_params_entries_iter(*this); +} + +template +inline bool url_search_params_iter::has_next() const { + return pos < params.params.size(); +} + +template <> +inline std::optional url_search_params_keys_iter::next() { + if (!has_next()) { + return std::nullopt; + } + return params.params[pos++].first; +} + +template <> +inline std::optional url_search_params_values_iter::next() { + if (!has_next()) { + return std::nullopt; + } + return params.params[pos++].second; +} + +template <> +inline std::optional +url_search_params_entries_iter::next() { + if (!has_next()) { + return std::nullopt; + } + return params.params[pos++]; +} + +} // namespace ada + +#endif // ADA_URL_SEARCH_PARAMS_INL_H +/* end file include/ada/url_search_params-inl.h */ +/* begin file include/ada/url_pattern-inl.h */ +/** + * @file url_pattern-inl.h + * @brief Declaration for the URLPattern inline functions. + */ +#ifndef ADA_URL_PATTERN_INL_H +#define ADA_URL_PATTERN_INL_H + + +#include + +namespace ada { + +inline bool url_pattern_init::operator==(const url_pattern_init& other) const { + return protocol == other.protocol && username == other.username && + password == other.password && hostname == other.hostname && + port == other.port && search == other.search && hash == other.hash && + pathname == other.pathname; +} + +inline bool url_pattern_component_result::operator==( + const url_pattern_component_result& other) const { + return input == other.input && groups == other.groups; +} + +template +std::string url_pattern_component::to_string() const { +#ifdef ADA_HAS_FORMAT + return std::format(R"({{"pattern": "{}", "has_regexp_groups": {}}})", pattern, + has_regexp_groups ? "true" : "false" //, + ); +#else + return ""; +#endif +} + +template +url_pattern_component_result +url_pattern_component::create_component_match_result( + std::string_view input, std::vector&& exec_result) { + // Let result be a new URLPatternComponentResult. + // Set result["input"] to input. + // Let groups be a record. + auto result = + url_pattern_component_result{.input = std::string(input), .groups = {}}; + + // If input is empty, then groups will always be empty. + if (input.empty() || exec_result.empty()) { + return result; + } + + // Optimization: Let's reserve the size. + result.groups.reserve(exec_result.size() - 1); + + // We explicitly start iterating from 0 even though the spec + // says we should start from 1. This case is handled by the + // std_regex_provider. + for (size_t index = 0; index < exec_result.size(); index++) { + result.groups.insert({ + group_name_list[index], + std::move(exec_result[index]), + }); + } + return result; +} + +template +std::string url_pattern::to_string() const { +#ifdef ADA_HAS_FORMAT + return std::format( + R"({{"protocol_component": "{}", "username_component": {}, "password_component": {}, "hostname_component": {}, "port_component": {}, "pathname_component": {}, "search_component": {}, "hash_component": {}, "ignore_case": {}}})", + protocol_component.to_string(), username_component.to_string(), + password_component.to_string(), hostname_component.to_string(), + port_component.to_string(), pathname_component.to_string(), + search_component.to_string(), hash_component.to_string(), + ignore_case_ ? "true" : "false"); +#else + return ""; +#endif +} + +template +std::string_view url_pattern::get_protocol() const + ada_lifetime_bound { + // Return this's associated URL pattern's protocol component's pattern string. + return protocol_component.pattern; +} +template +std::string_view url_pattern::get_username() const + ada_lifetime_bound { + // Return this's associated URL pattern's username component's pattern string. + return username_component.pattern; +} +template +std::string_view url_pattern::get_password() const + ada_lifetime_bound { + // Return this's associated URL pattern's password component's pattern string. + return password_component.pattern; +} +template +std::string_view url_pattern::get_hostname() const + ada_lifetime_bound { + // Return this's associated URL pattern's hostname component's pattern string. + return hostname_component.pattern; +} +template +std::string_view url_pattern::get_port() const + ada_lifetime_bound { + // Return this's associated URL pattern's port component's pattern string. + return port_component.pattern; +} +template +std::string_view url_pattern::get_pathname() const + ada_lifetime_bound { + // Return this's associated URL pattern's pathname component's pattern string. + return pathname_component.pattern; +} +template +std::string_view url_pattern::get_search() const + ada_lifetime_bound { + // Return this's associated URL pattern's search component's pattern string. + return search_component.pattern; +} +template +std::string_view url_pattern::get_hash() const + ada_lifetime_bound { + // Return this's associated URL pattern's hash component's pattern string. + return hash_component.pattern; +} +template +bool url_pattern::ignore_case() const { + return ignore_case_; +} +template +bool url_pattern::has_regexp_groups() const { + // If this's associated URL pattern's has regexp groups, then return true. + return protocol_component.has_regexp_groups || + username_component.has_regexp_groups || + password_component.has_regexp_groups || + hostname_component.has_regexp_groups || + port_component.has_regexp_groups || + pathname_component.has_regexp_groups || + search_component.has_regexp_groups || hash_component.has_regexp_groups; +} + +inline bool url_pattern_part::is_regexp() const noexcept { + return type == url_pattern_part_type::REGEXP; +} + +inline std::string_view url_pattern_compile_component_options::get_delimiter() + const { + if (delimiter) { + return {&delimiter.value(), 1}; + } + return {}; +} + +inline std::string_view url_pattern_compile_component_options::get_prefix() + const { + if (prefix) { + return {&prefix.value(), 1}; + } + return {}; +} + +template +template +tl::expected, errors> +url_pattern_component::compile( + std::string_view input, F& encoding_callback, + url_pattern_compile_component_options& options) { + ada_log("url_pattern_component::compile input: ", input); + // Let part list be the result of running parse a pattern string given input, + // options, and encoding callback. + auto part_list = url_pattern_helpers::parse_pattern_string(input, options, + encoding_callback); + + if (!part_list) { + ada_log("parse_pattern_string failed"); + return tl::unexpected(part_list.error()); + } + + // Let (regular expression string, name list) be the result of running + // generate a regular expression and name list given part list and options. + auto [regular_expression_string, name_list] = + url_pattern_helpers::generate_regular_expression_and_name_list(*part_list, + options); + + ada_log("regular expression string: ", regular_expression_string); + + // Let pattern string be the result of running generate a pattern + // string given part list and options. + auto pattern_string = + url_pattern_helpers::generate_pattern_string(*part_list, options); + + // Let regular expression be RegExpCreate(regular expression string, + // flags). If this throws an exception, catch it, and throw a + // TypeError. + std::optional regular_expression = + regex_provider::create_instance(regular_expression_string, + options.ignore_case); + + if (!regular_expression) { + return tl::unexpected(errors::type_error); + } + + // For each part of part list: + // - If part’s type is "regexp", then set has regexp groups to true. + const auto has_regexp = [](const auto& part) { return part.is_regexp(); }; + const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp); + + ada_log("has regexp groups: ", has_regexp_groups); + + // Return a new component whose pattern string is pattern string, regular + // expression is regular expression, group name list is name list, and has + // regexp groups is has regexp groups. + return url_pattern_component( + std::move(pattern_string), std::move(*regular_expression), + std::move(name_list), has_regexp_groups); +} + +template +result> url_pattern::exec( + const url_pattern_input& input, std::string_view* base_url) { + // Return the result of match given this's associated URL pattern, input, and + // baseURL if given. + return match(input, base_url); +} + +template +result url_pattern::test(const url_pattern_input& input, + std::string_view* base_url) { + // TODO: Optimization opportunity. Rather than returning `url_pattern_result` + // Implement a fast path just like `can_parse()` in ada_url. + // Let result be the result of match given this's associated URL pattern, + // input, and baseURL if given. + // If result is null, return false. + if (auto result = match(input, base_url); result.has_value()) { + return result->has_value(); + } + return tl::unexpected(errors::type_error); +} + +template +result> url_pattern::match( + const url_pattern_input& input, std::string_view* base_url_string) { + std::string protocol{}; + std::string username{}; + std::string password{}; + std::string hostname{}; + std::string port{}; + std::string pathname{}; + std::string search{}; + std::string hash{}; + + // Let inputs be an empty list. + // Append input to inputs. + std::vector inputs{input}; + + // If input is a URLPatternInit then: + if (std::holds_alternative(input)) { + ada_log( + "url_pattern::match called with url_pattern_init and base_url_string=", + base_url_string); + // If baseURLString was given, throw a TypeError. + if (base_url_string) { + ada_log("failed to match because base_url_string was given"); + return tl::unexpected(errors::type_error); + } + + // Let applyResult be the result of process a URLPatternInit given input, + // "url", protocol, username, password, hostname, port, pathname, search, + // and hash. + auto apply_result = url_pattern_init::process( + std::get(input), "url", protocol, username, password, + hostname, port, pathname, search, hash); + + // If this throws an exception, catch it, and return null. + if (!apply_result.has_value()) { + ada_log("match returned std::nullopt because process threw"); + return std::nullopt; + } + + // Set protocol to applyResult["protocol"]. + ADA_ASSERT_TRUE(apply_result->protocol.has_value()); + protocol = apply_result->protocol.value(); + + // Set username to applyResult["username"]. + ADA_ASSERT_TRUE(apply_result->username.has_value()); + username = apply_result->username.value(); + + // Set password to applyResult["password"]. + ADA_ASSERT_TRUE(apply_result->password.has_value()); + password = apply_result->password.value(); + + // Set hostname to applyResult["hostname"]. + ADA_ASSERT_TRUE(apply_result->hostname.has_value()); + hostname = apply_result->hostname.value(); + + // Set port to applyResult["port"]. + ADA_ASSERT_TRUE(apply_result->port.has_value()); + port = apply_result->port.value(); + + // Set pathname to applyResult["pathname"]. + ADA_ASSERT_TRUE(apply_result->pathname.has_value()); + pathname = apply_result->pathname.value(); - if (equal == std::string_view::npos) { - std::string name(current); - std::replace(name.begin(), name.end(), '+', ' '); - params.emplace_back(unicode::percent_decode(name, name.find('%')), ""); + // Set search to applyResult["search"]. + ADA_ASSERT_TRUE(apply_result->search.has_value()); + if (apply_result->search->starts_with("?")) { + search = apply_result->search->substr(1); } else { - std::string name(current.substr(0, equal)); - std::string value(current.substr(equal + 1)); + search = apply_result->search.value(); + } - std::replace(name.begin(), name.end(), '+', ' '); - std::replace(value.begin(), value.end(), '+', ' '); + // Set hash to applyResult["hash"]. + ADA_ASSERT_TRUE(apply_result->hash.has_value()); + ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#")); + hash = apply_result->hash.value(); + } else { + ADA_ASSERT_TRUE(std::holds_alternative(input)); - params.emplace_back(unicode::percent_decode(name, name.find('%')), - unicode::percent_decode(value, value.find('%'))); - } - }; + // Let baseURL be null. + result base_url; - while (!input.empty()) { - auto ampersand_index = input.find('&'); + // If baseURLString was given, then: + if (base_url_string) { + // Let baseURL be the result of parsing baseURLString. + base_url = ada::parse(*base_url_string, nullptr); - if (ampersand_index == std::string_view::npos) { - if (!input.empty()) { - process_key_value(input); + // If baseURL is failure, return null. + if (!base_url) { + ada_log("match returned std::nullopt because failed to parse base_url=", + *base_url_string); + return std::nullopt; } - break; - } else if (ampersand_index != 0) { - process_key_value(input.substr(0, ampersand_index)); + + // Append baseURLString to inputs. + inputs.emplace_back(*base_url_string); } - input.remove_prefix(ampersand_index + 1); - } -} + url_aggregator* base_url_value = + base_url.has_value() ? &base_url.value() : nullptr; -inline void url_search_params::append(const std::string_view key, - const std::string_view value) { - params.emplace_back(key, value); -} + // Set url to the result of parsing input given baseURL. + auto url = ada::parse(std::get(input), + base_url_value); -inline size_t url_search_params::size() const noexcept { return params.size(); } + // If url is failure, return null. + if (!url) { + ada_log("match returned std::nullopt because url failed"); + return std::nullopt; + } -inline std::optional url_search_params::get( - const std::string_view key) { - auto entry = std::find_if(params.begin(), params.end(), - [&key](auto ¶m) { return param.first == key; }); + // Set protocol to url’s scheme. + // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':' + // is removed. Similar work was done on workerd: + // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038 + protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1); + // Set username to url’s username. + username = url->get_username(); + // Set password to url’s password. + password = url->get_password(); + // Set hostname to url’s host, serialized, or the empty string if the value + // is null. + hostname = url->get_hostname(); + // Set port to url’s port, serialized, or the empty string if the value is + // null. + port = url->get_port(); + // Set pathname to the result of URL path serializing url. + pathname = url->get_pathname(); + // Set search to url’s query or the empty string if the value is null. + // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?' + // is removed. Similar work was done on workerd: + // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232 + if (url->has_search()) { + ADA_ASSERT_TRUE(url->get_search().starts_with("?")); + search = url->get_search().substr(1); + } else { + search = ""; + } + // Set hash to url’s fragment or the empty string if the value is null. + // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is + // removed. Similar work was done on workerd: + // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242 + if (url->has_hash()) { + ADA_ASSERT_TRUE(url->get_hash().starts_with("#")); + hash = url->get_hash().substr(1); + } else { + hash = ""; + } + } - if (entry == params.end()) { + // Let protocolExecResult be RegExpBuiltinExec(urlPattern’s protocol + // component's regular expression, protocol). + auto protocol_exec_result = + regex_provider::regex_search(protocol, protocol_component.regexp); + + // Let usernameExecResult be RegExpBuiltinExec(urlPattern’s username + // component's regular expression, username). + auto username_exec_result = + regex_provider::regex_search(username, username_component.regexp); + + // Let passwordExecResult be RegExpBuiltinExec(urlPattern’s password + // component's regular expression, password). + auto password_exec_result = + regex_provider::regex_search(password, password_component.regexp); + + // Let hostnameExecResult be RegExpBuiltinExec(urlPattern’s hostname + // component's regular expression, hostname). + auto hostname_exec_result = + regex_provider::regex_search(hostname, hostname_component.regexp); + + // Let portExecResult be RegExpBuiltinExec(urlPattern’s port component's + // regular expression, port). + auto port_exec_result = + regex_provider::regex_search(port, port_component.regexp); + + // Let pathnameExecResult be RegExpBuiltinExec(urlPattern’s pathname + // component's regular expression, pathname). + auto pathname_exec_result = + regex_provider::regex_search(pathname, pathname_component.regexp); + + // Let searchExecResult be RegExpBuiltinExec(urlPattern’s search component's + // regular expression, search). + auto search_exec_result = + regex_provider::regex_search(search, search_component.regexp); + + // Let hashExecResult be RegExpBuiltinExec(urlPattern’s hash component's + // regular expression, hash). + auto hash_exec_result = + regex_provider::regex_search(hash, hash_component.regexp); + + // If protocolExecResult, usernameExecResult, passwordExecResult, + // hostnameExecResult, portExecResult, pathnameExecResult, searchExecResult, + // or hashExecResult are null then return null. + if (!protocol_exec_result || !username_exec_result || !password_exec_result || + !hostname_exec_result || !port_exec_result || !pathname_exec_result || + !search_exec_result || !hash_exec_result) { return std::nullopt; } - return entry->second; -} + // Let result be a new URLPatternResult. + auto result = url_pattern_result{}; + // Set result["inputs"] to inputs. + result.inputs = std::move(inputs); + // Set result["protocol"] to the result of creating a component match result + // given urlPattern’s protocol component, protocol, and protocolExecResult. + result.protocol = protocol_component.create_component_match_result( + protocol, std::move(*protocol_exec_result)); -inline std::vector url_search_params::get_all( - const std::string_view key) { - std::vector out{}; + // Set result["username"] to the result of creating a component match result + // given urlPattern’s username component, username, and usernameExecResult. + result.username = username_component.create_component_match_result( + username, std::move(*username_exec_result)); - for (auto ¶m : params) { - if (param.first == key) { - out.emplace_back(param.second); - } - } + // Set result["password"] to the result of creating a component match result + // given urlPattern’s password component, password, and passwordExecResult. + result.password = password_component.create_component_match_result( + password, std::move(*password_exec_result)); - return out; -} + // Set result["hostname"] to the result of creating a component match result + // given urlPattern’s hostname component, hostname, and hostnameExecResult. + result.hostname = hostname_component.create_component_match_result( + hostname, std::move(*hostname_exec_result)); -inline bool url_search_params::has(const std::string_view key) noexcept { - auto entry = std::find_if(params.begin(), params.end(), - [&key](auto ¶m) { return param.first == key; }); - return entry != params.end(); -} + // Set result["port"] to the result of creating a component match result given + // urlPattern’s port component, port, and portExecResult. + result.port = port_component.create_component_match_result( + port, std::move(*port_exec_result)); -inline bool url_search_params::has(std::string_view key, - std::string_view value) noexcept { - auto entry = - std::find_if(params.begin(), params.end(), [&key, &value](auto ¶m) { - return param.first == key && param.second == value; - }); - return entry != params.end(); -} + // Set result["pathname"] to the result of creating a component match result + // given urlPattern’s pathname component, pathname, and pathnameExecResult. + result.pathname = pathname_component.create_component_match_result( + pathname, std::move(*pathname_exec_result)); -inline std::string url_search_params::to_string() const { - auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE; - std::string out{}; - for (size_t i = 0; i < params.size(); i++) { - auto key = ada::unicode::percent_encode(params[i].first, character_set); - auto value = ada::unicode::percent_encode(params[i].second, character_set); + // Set result["search"] to the result of creating a component match result + // given urlPattern’s search component, search, and searchExecResult. + result.search = search_component.create_component_match_result( + search, std::move(*search_exec_result)); - // Performance optimization: Move this inside percent_encode. - std::replace(key.begin(), key.end(), ' ', '+'); - std::replace(value.begin(), value.end(), ' ', '+'); + // Set result["hash"] to the result of creating a component match result given + // urlPattern’s hash component, hash, and hashExecResult. + result.hash = hash_component.create_component_match_result( + hash, std::move(*hash_exec_result)); - if (i != 0) { - out += "&"; - } - out.append(key); - out += "="; - out.append(value); - } - return out; + return result; } -inline void url_search_params::set(const std::string_view key, - const std::string_view value) { - const auto find = [&key](auto ¶m) { return param.first == key; }; +} // namespace ada + +#endif +/* end file include/ada/url_pattern-inl.h */ +/* begin file include/ada/url_pattern_helpers-inl.h */ +/** + * @file url_pattern_helpers-inl.h + * @brief Declaration for the URLPattern helpers. + */ +#ifndef ADA_URL_PATTERN_HELPERS_INL_H +#define ADA_URL_PATTERN_HELPERS_INL_H - auto it = std::find_if(params.begin(), params.end(), find); +#include +#include - if (it == params.end()) { - params.emplace_back(key, value); - } else { - it->second = value; - params.erase(std::remove_if(std::next(it), params.end(), find), - params.end()); + +namespace ada::url_pattern_helpers { +inline std::string to_string(token_type type) { + switch (type) { + case token_type::INVALID_CHAR: + return "INVALID_CHAR"; + case token_type::OPEN: + return "OPEN"; + case token_type::CLOSE: + return "CLOSE"; + case token_type::REGEXP: + return "REGEXP"; + case token_type::NAME: + return "NAME"; + case token_type::CHAR: + return "CHAR"; + case token_type::ESCAPED_CHAR: + return "ESCAPED_CHAR"; + case token_type::OTHER_MODIFIER: + return "OTHER_MODIFIER"; + case token_type::ASTERISK: + return "ASTERISK"; + case token_type::END: + return "END"; + default: + ada::unreachable(); + } +} + +template +void constructor_string_parser::rewind() { + // Set parser’s token index to parser’s component start. + token_index = component_start; + // Set parser’s token increment to 0. + token_increment = 0; +} + +template +bool constructor_string_parser::is_hash_prefix() { + // Return the result of running is a non-special pattern char given parser, + // parser’s token index and "#". + return is_non_special_pattern_char(token_index, "#"); +} + +template +bool constructor_string_parser::is_search_prefix() { + // If result of running is a non-special pattern char given parser, parser’s + // token index and "?" is true, then return true. + if (is_non_special_pattern_char(token_index, "?")) { + return true; } -} -inline void url_search_params::remove(const std::string_view key) { - params.erase( - std::remove_if(params.begin(), params.end(), - [&key](auto ¶m) { return param.first == key; }), - params.end()); -} + // If parser’s token list[parser’s token index]'s value is not "?", then + // return false. + if (token_list[token_index].value != "?") { + return false; + } -inline void url_search_params::remove(const std::string_view key, - const std::string_view value) { - params.erase(std::remove_if(params.begin(), params.end(), - [&key, &value](auto ¶m) { - return param.first == key && - param.second == value; - }), - params.end()); -} + // If previous index is less than 0, then return true. + if (token_index == 0) return true; + // Let previous index be parser’s token index − 1. + auto previous_index = token_index - 1; + // Let previous token be the result of running get a safe token given parser + // and previous index. + auto previous_token = get_safe_token(previous_index); + ADA_ASSERT_TRUE(previous_token); + // If any of the following are true, then return false: + // - previous token’s type is "name". + // - previous token’s type is "regexp". + // - previous token’s type is "close". + // - previous token’s type is "asterisk". + return !(previous_token->type == token_type::NAME || + previous_token->type == token_type::REGEXP || + previous_token->type == token_type::CLOSE || + previous_token->type == token_type::ASTERISK); +} + +template +bool constructor_string_parser::is_non_special_pattern_char( + size_t index, std::string_view value) { + // Let token be the result of running get a safe token given parser and index. + auto token = get_safe_token(index); + ADA_ASSERT_TRUE(token); + + // If token’s value is not value, then return false. + if (token->value != value) { + return false; + } -inline void url_search_params::sort() { - std::stable_sort(params.begin(), params.end(), - [](const key_value_pair &lhs, const key_value_pair &rhs) { - return lhs.first < rhs.first; - }); + // If any of the following are true: + // - token’s type is "char"; + // - token’s type is "escaped-char"; or + // - token’s type is "invalid-char", + // - then return true. + return token->type == token_type::CHAR || + token->type == token_type::ESCAPED_CHAR || + token->type == token_type::INVALID_CHAR; } -inline url_search_params_keys_iter url_search_params::get_keys() { - return url_search_params_keys_iter(*this); -} +template +const Token* constructor_string_parser::get_safe_token( + size_t index) { + // If index is less than parser’s token list's size, then return parser’s + // token list[index]. + if (index < token_list.size()) [[likely]] { + return &token_list[index]; + } -/** - * @see https://url.spec.whatwg.org/#interface-urlsearchparams - */ -inline url_search_params_values_iter url_search_params::get_values() { - return url_search_params_values_iter(*this); + // Assert: parser’s token list's size is greater than or equal to 1. + ADA_ASSERT_TRUE(!token_list.empty()); + + // Let token be parser’s token list[last index]. + // Assert: token’s type is "end". + ADA_ASSERT_TRUE(token_list.back().type == token_type::END); + + // Return token. + return &token_list.back(); } -/** - * @see https://url.spec.whatwg.org/#interface-urlsearchparams - */ -inline url_search_params_entries_iter url_search_params::get_entries() { - return url_search_params_entries_iter(*this); +template +bool constructor_string_parser::is_group_open() const { + // If parser’s token list[parser’s token index]'s type is "open", then return + // true. + return token_list[token_index].type == token_type::OPEN; } -template -inline bool url_search_params_iter::has_next() { - return pos < params.params.size(); +template +bool constructor_string_parser::is_group_close() const { + // If parser’s token list[parser’s token index]'s type is "close", then return + // true. + return token_list[token_index].type == token_type::CLOSE; } -template <> -inline std::optional url_search_params_keys_iter::next() { - if (!has_next()) { - return std::nullopt; +template +bool constructor_string_parser::next_is_authority_slashes() { + // If the result of running is a non-special pattern char given parser, + // parser’s token index + 1, and "/" is false, then return false. + if (!is_non_special_pattern_char(token_index + 1, "/")) { + return false; + } + // If the result of running is a non-special pattern char given parser, + // parser’s token index + 2, and "/" is false, then return false. + if (!is_non_special_pattern_char(token_index + 2, "/")) { + return false; + } + return true; +} + +template +bool constructor_string_parser::is_protocol_suffix() { + // Return the result of running is a non-special pattern char given parser, + // parser’s token index, and ":". + return is_non_special_pattern_char(token_index, ":"); +} + +template +void constructor_string_parser::change_state(State new_state, + size_t skip) { + // If parser’s state is not "init", not "authority", and not "done", then set + // parser’s result[parser’s state] to the result of running make a component + // string given parser. + if (state != State::INIT && state != State::AUTHORITY && + state != State::DONE) { + auto value = make_component_string(); + // TODO: Simplify this. + switch (state) { + case State::PROTOCOL: { + result.protocol = value; + break; + } + case State::USERNAME: { + result.username = value; + break; + } + case State::PASSWORD: { + result.password = value; + break; + } + case State::HOSTNAME: { + result.hostname = value; + break; + } + case State::PORT: { + result.port = value; + break; + } + case State::PATHNAME: { + result.pathname = value; + break; + } + case State::SEARCH: { + result.search = value; + break; + } + case State::HASH: { + result.hash = value; + break; + } + default: + ada::unreachable(); + } } - return params.params[pos++].first; -} -template <> -inline std::optional url_search_params_values_iter::next() { - if (!has_next()) { - return std::nullopt; + // If parser’s state is not "init" and new state is not "done", then: + if (state != State::INIT && new_state != State::DONE) { + // If parser’s state is "protocol", "authority", "username", or "password"; + // new state is "port", "pathname", "search", or "hash"; and parser’s + // result["hostname"] does not exist, then set parser’s result["hostname"] + // to the empty string. + if ((state == State::PROTOCOL || state == State::AUTHORITY || + state == State::USERNAME || state == State::PASSWORD) && + (new_state == State::PORT || new_state == State::PATHNAME || + new_state == State::SEARCH || new_state == State::HASH) && + !result.hostname) + result.hostname = ""; + } + + // If parser’s state is "protocol", "authority", "username", "password", + // "hostname", or "port"; new state is "search" or "hash"; and parser’s + // result["pathname"] does not exist, then: + if ((state == State::PROTOCOL || state == State::AUTHORITY || + state == State::USERNAME || state == State::PASSWORD || + state == State::HOSTNAME || state == State::PORT) && + (new_state == State::SEARCH || new_state == State::HASH) && + !result.pathname) { + if (protocol_matches_a_special_scheme_flag) { + result.pathname = "/"; + } else { + // Otherwise, set parser’s result["pathname"] to the empty string. + result.pathname = ""; + } } - return params.params[pos++].second; -} -template <> -inline std::optional -url_search_params_entries_iter::next() { - if (!has_next()) { + // If parser’s state is "protocol", "authority", "username", "password", + // "hostname", "port", or "pathname"; new state is "hash"; and parser’s + // result["search"] does not exist, then set parser’s result["search"] to + // the empty string. + if ((state == State::PROTOCOL || state == State::AUTHORITY || + state == State::USERNAME || state == State::PASSWORD || + state == State::HOSTNAME || state == State::PORT || + state == State::PATHNAME) && + new_state == State::HASH && !result.search) { + result.search = ""; + } + + // Set parser’s state to new state. + state = new_state; + // Increment parser’s token index by skip. + token_index += skip; + // Set parser’s component start to parser’s token index. + component_start = token_index; + // Set parser’s token increment to 0. + token_increment = 0; +} + +template +std::string constructor_string_parser::make_component_string() { + // Assert: parser’s token index is less than parser’s token list's size. + ADA_ASSERT_TRUE(token_index < token_list.size()); + + // Let token be parser’s token list[parser’s token index]. + // Let end index be token’s index. + const auto end_index = token_list[token_index].index; + // Let component start token be the result of running get a safe token given + // parser and parser’s component start. + const auto component_start_token = get_safe_token(component_start); + ADA_ASSERT_TRUE(component_start_token); + // Let component start input index be component start token’s index. + const auto component_start_input_index = component_start_token->index; + // Return the code point substring from component start input index to end + // index within parser’s input. + return input.substr(component_start_input_index, + end_index - component_start_input_index); +} + +template +bool constructor_string_parser::is_an_identity_terminator() { + // Return the result of running is a non-special pattern char given parser, + // parser’s token index, and "@". + return is_non_special_pattern_char(token_index, "@"); +} + +template +bool constructor_string_parser::is_pathname_start() { + // Return the result of running is a non-special pattern char given parser, + // parser’s token index, and "/". + return is_non_special_pattern_char(token_index, "/"); +} + +template +bool constructor_string_parser::is_password_prefix() { + // Return the result of running is a non-special pattern char given parser, + // parser’s token index, and ":". + return is_non_special_pattern_char(token_index, ":"); +} + +template +bool constructor_string_parser::is_an_ipv6_open() { + // Return the result of running is a non-special pattern char given parser, + // parser’s token index, and "[". + return is_non_special_pattern_char(token_index, "["); +} + +template +bool constructor_string_parser::is_an_ipv6_close() { + // Return the result of running is a non-special pattern char given parser, + // parser’s token index, and "]". + return is_non_special_pattern_char(token_index, "]"); +} + +template +bool constructor_string_parser::is_port_prefix() { + // Return the result of running is a non-special pattern char given parser, + // parser’s token index, and ":". + return is_non_special_pattern_char(token_index, ":"); +} + +inline void Tokenizer::get_next_code_point() { + ada_log("Tokenizer::get_next_code_point called with index=", next_index); + ADA_ASSERT_TRUE(next_index < input.size()); + // this assumes that we have a valid, non-truncated UTF-8 stream. + code_point = 0; + size_t number_bytes = 0; + unsigned char first_byte = input[next_index]; + + if ((first_byte & 0x80) == 0) { + // 1-byte character (ASCII) + next_index++; + code_point = first_byte; + ada_log("Tokenizer::get_next_code_point returning ASCII code point=", + uint32_t(code_point)); + ada_log("Tokenizer::get_next_code_point next_index =", next_index, + " input.size()=", input.size()); + return; + } + ada_log("Tokenizer::get_next_code_point read first byte=", + uint32_t(first_byte)); + if ((first_byte & 0xE0) == 0xC0) { + code_point = first_byte & 0x1F; + number_bytes = 2; + ada_log("Tokenizer::get_next_code_point two bytes"); + } else if ((first_byte & 0xF0) == 0xE0) { + code_point = first_byte & 0x0F; + number_bytes = 3; + ada_log("Tokenizer::get_next_code_point three bytes"); + } else if ((first_byte & 0xF8) == 0xF0) { + code_point = first_byte & 0x07; + number_bytes = 4; + ada_log("Tokenizer::get_next_code_point four bytes"); + } + ADA_ASSERT_TRUE(number_bytes + next_index <= input.size()); + + for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) { + unsigned char byte = input[i]; + ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte)); + code_point = (code_point << 6) | (byte & 0x3F); + } + ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=", + uint32_t(code_point)); + ada_log("Tokenizer::get_next_code_point next_index =", next_index, + " input.size()=", input.size()); + next_index += number_bytes; +} + +inline void Tokenizer::seek_and_get_next_code_point(size_t new_index) { + ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=", + new_index); + // Set tokenizer’s next index to index. + next_index = new_index; + // Run get the next code point given tokenizer. + get_next_code_point(); +} + +inline void Tokenizer::add_token(token_type type, size_t next_position, + size_t value_position, size_t value_length) { + ada_log("Tokenizer::add_token called with type=", to_string(type), + " next_position=", next_position, " value_position=", value_position); + ADA_ASSERT_TRUE(next_position >= value_position); + + // Let token be a new token. + // Set token’s type to type. + // Set token’s index to tokenizer’s index. + // Set token’s value to the code point substring from value position with + // length value length within tokenizer’s input. + // Append token to the back of tokenizer’s token list. + token_list.emplace_back(type, index, + input.substr(value_position, value_length)); + // Set tokenizer’s index to next position. + index = next_position; +} + +inline void Tokenizer::add_token_with_default_length(token_type type, + size_t next_position, + size_t value_position) { + // Let computed length be next position − value position. + auto computed_length = next_position - value_position; + // Run add a token given tokenizer, type, next position, value position, and + // computed length. + add_token(type, next_position, value_position, computed_length); +} + +inline void Tokenizer::add_token_with_defaults(token_type type) { + ada_log("Tokenizer::add_token_with_defaults called with type=", + to_string(type)); + // Run add a token with default length given tokenizer, type, tokenizer’s next + // index, and tokenizer’s index. + add_token_with_default_length(type, next_index, index); +} + +inline ada_warn_unused std::optional +Tokenizer::process_tokenizing_error(size_t next_position, + size_t value_position) { + // If tokenizer’s policy is "strict", then throw a TypeError. + if (policy == token_policy::STRICT) { + ada_log("process_tokenizing_error failed with next_position=", + next_position, " value_position=", value_position); + return errors::type_error; + } + // Assert: tokenizer’s policy is "lenient". + ADA_ASSERT_TRUE(policy == token_policy::LENIENT); + // Run add a token with default length given tokenizer, "invalid-char", next + // position, and value position. + add_token_with_default_length(token_type::INVALID_CHAR, next_position, + value_position); + return std::nullopt; +} + +template +Token* url_pattern_parser::try_consume_modifier_token() { + // Let token be the result of running try to consume a token given parser and + // "other-modifier". + auto token = try_consume_token(token_type::OTHER_MODIFIER); + // If token is not null, then return token. + if (token) return token; + // Set token to the result of running try to consume a token given parser and + // "asterisk". + // Return token. + return try_consume_token(token_type::ASTERISK); +} + +template +Token* url_pattern_parser::try_consume_regexp_or_wildcard_token( + Token* name_token) { + // Let token be the result of running try to consume a token given parser and + // "regexp". + auto token = try_consume_token(token_type::REGEXP); + // If name token is null and token is null, then set token to the result of + // running try to consume a token given parser and "asterisk". + if (!name_token && !token) { + token = try_consume_token(token_type::ASTERISK); + } + // Return token. + return token; +} + +template +Token* url_pattern_parser::try_consume_token(token_type type) { + ada_log("url_pattern_parser::try_consume_token called with type=", + to_string(type)); + // Assert: parser’s index is less than parser’s token list size. + ADA_ASSERT_TRUE(index < tokens.size()); + // Let next token be parser’s token list[parser’s index]. + auto& next_token = tokens[index]; + // If next token’s type is not type return null. + if (next_token.type != type) return nullptr; + // Increase parser’s index by 1. + index++; + // Return next token. + return &next_token; +} + +template +std::string url_pattern_parser::consume_text() { + // Let result be the empty string. + std::string result{}; + // While true: + while (true) { + // Let token be the result of running try to consume a token given parser + // and "char". + auto token = try_consume_token(token_type::CHAR); + // If token is null, then set token to the result of running try to consume + // a token given parser and "escaped-char". + if (!token) token = try_consume_token(token_type::ESCAPED_CHAR); + // If token is null, then break. + if (!token) break; + // Append token’s value to the end of result. + result.append(token->value); + } + // Return result. + return result; +} + +template +bool url_pattern_parser::consume_required_token(token_type type) { + ada_log("url_pattern_parser::consume_required_token called with type=", + to_string(type)); + // Let result be the result of running try to consume a token given parser and + // type. + return try_consume_token(type) != nullptr; +} + +template +std::optional +url_pattern_parser::maybe_add_part_from_the_pending_fixed_value() { + // If parser’s pending fixed value is the empty string, then return. + if (pending_fixed_value.empty()) { + ada_log("pending_fixed_value is empty"); return std::nullopt; } - return params.params[pos++]; + // Let encoded value be the result of running parser’s encoding callback given + // parser’s pending fixed value. + auto encoded_value = encoding_callback(pending_fixed_value); + if (!encoded_value) { + ada_log("failed to encode pending_fixed_value: ", pending_fixed_value); + return encoded_value.error(); + } + // Set parser’s pending fixed value to the empty string. + pending_fixed_value.clear(); + // Let part be a new part whose type is "fixed-text", value is encoded value, + // and modifier is "none". + // Append part to parser’s part list. + parts.emplace_back(url_pattern_part_type::FIXED_TEXT, + std::move(*encoded_value), + url_pattern_part_modifier::NONE); + return std::nullopt; +} + +template +std::optional url_pattern_parser::add_part( + std::string_view prefix, Token* name_token, Token* regexp_or_wildcard_token, + std::string_view suffix, Token* modifier_token) { + // Let modifier be "none". + auto modifier = url_pattern_part_modifier::NONE; + // If modifier token is not null: + if (modifier_token) { + // If modifier token’s value is "?" then set modifier to "optional". + if (modifier_token->value == "?") { + modifier = url_pattern_part_modifier::OPTIONAL; + } else if (modifier_token->value == "*") { + // Otherwise if modifier token’s value is "*" then set modifier to + // "zero-or-more". + modifier = url_pattern_part_modifier::ZERO_OR_MORE; + } else if (modifier_token->value == "+") { + // Otherwise if modifier token’s value is "+" then set modifier to + // "one-or-more". + modifier = url_pattern_part_modifier::ONE_OR_MORE; + } + } + // If name token is null and regexp or wildcard token is null and modifier + // is "none": + if (!name_token && !regexp_or_wildcard_token && + modifier == url_pattern_part_modifier::NONE) { + // Append prefix to the end of parser’s pending fixed value. + pending_fixed_value.append(prefix); + return std::nullopt; + } + // Run maybe add a part from the pending fixed value given parser. + if (auto error = maybe_add_part_from_the_pending_fixed_value()) { + return *error; + } + // If name token is null and regexp or wildcard token is null: + if (!name_token && !regexp_or_wildcard_token) { + // Assert: suffix is the empty string. + ADA_ASSERT_TRUE(suffix.empty()); + // If prefix is the empty string, then return. + if (prefix.empty()) return std::nullopt; + // Let encoded value be the result of running parser’s encoding callback + // given prefix. + auto encoded_value = encoding_callback(prefix); + if (!encoded_value) { + return encoded_value.error(); + } + // Let part be a new part whose type is "fixed-text", value is encoded + // value, and modifier is modifier. + // Append part to parser’s part list. + parts.emplace_back(url_pattern_part_type::FIXED_TEXT, + std::move(*encoded_value), modifier); + return std::nullopt; + } + // Let regexp value be the empty string. + std::string regexp_value{}; + // If regexp or wildcard token is null, then set regexp value to parser’s + // segment wildcard regexp. + if (!regexp_or_wildcard_token) { + regexp_value = segment_wildcard_regexp; + } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) { + // Otherwise if regexp or wildcard token’s type is "asterisk", then set + // regexp value to the full wildcard regexp value. + regexp_value = ".*"; + } else { + // Otherwise set regexp value to regexp or wildcard token’s value. + regexp_value = regexp_or_wildcard_token->value; + } + // Let type be "regexp". + auto type = url_pattern_part_type::REGEXP; + // If regexp value is parser’s segment wildcard regexp: + if (regexp_value == segment_wildcard_regexp) { + // Set type to "segment-wildcard". + type = url_pattern_part_type::SEGMENT_WILDCARD; + // Set regexp value to the empty string. + regexp_value.clear(); + } else if (regexp_value == ".*") { + // Otherwise if regexp value is the full wildcard regexp value: + // Set type to "full-wildcard". + type = url_pattern_part_type::FULL_WILDCARD; + // Set regexp value to the empty string. + regexp_value.clear(); + } + // Let name be the empty string. + std::string name{}; + // If name token is not null, then set name to name token’s value. + if (name_token) { + name = name_token->value; + } else if (regexp_or_wildcard_token) { + // Otherwise if regexp or wildcard token is not null: + // Set name to parser’s next numeric name, serialized. + // TODO: Make sure this is correct. + name = std::to_string(next_numeric_name); + // Increment parser’s next numeric name by 1. + next_numeric_name++; + } + // If the result of running is a duplicate name given parser and name is + // true, then throw a TypeError. + if (std::ranges::any_of( + parts, [&name](const auto& part) { return part.name == name; })) { + return errors::type_error; + } + // Let encoded prefix be the result of running parser’s encoding callback + // given prefix. + auto encoded_prefix = encoding_callback(prefix); + if (!encoded_prefix) return encoded_prefix.error(); + // Let encoded suffix be the result of running parser’s encoding callback + // given suffix. + auto encoded_suffix = encoding_callback(suffix); + if (!encoded_suffix) return encoded_suffix.error(); + // Let part be a new part whose type is type, value is regexp value, + // modifier is modifier, name is name, prefix is encoded prefix, and suffix + // is encoded suffix. + // Append part to parser’s part list. + parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name), + std::move(*encoded_prefix), std::move(*encoded_suffix)); + return std::nullopt; +} + +template +tl::expected, errors> parse_pattern_string( + std::string_view input, url_pattern_compile_component_options& options, + F& encoding_callback) { + ada_log("parse_pattern_string input=", input); + // Let parser be a new pattern parser whose encoding callback is encoding + // callback and segment wildcard regexp is the result of running generate a + // segment wildcard regexp given options. + auto parser = url_pattern_parser( + encoding_callback, generate_segment_wildcard_regexp(options)); + // Set parser’s token list to the result of running tokenize given input and + // "strict". + auto tokenize_result = tokenize(input, token_policy::STRICT); + if (!tokenize_result) { + ada_log("parse_pattern_string tokenize failed"); + return tl::unexpected(tokenize_result.error()); + } + parser.tokens = std::move(*tokenize_result); + + // While parser’s index is less than parser’s token list's size: + while (parser.can_continue()) { + // Let char token be the result of running try to consume a token given + // parser and "char". + auto char_token = parser.try_consume_token(token_type::CHAR); + // Let name token be the result of running try to consume a token given + // parser and "name". + auto name_token = parser.try_consume_token(token_type::NAME); + // Let regexp or wildcard token be the result of running try to consume a + // regexp or wildcard token given parser and name token. + auto regexp_or_wildcard_token = + parser.try_consume_regexp_or_wildcard_token(name_token); + // If name token is not null or regexp or wildcard token is not null: + if (name_token || regexp_or_wildcard_token) { + // Let prefix be the empty string. + std::string prefix{}; + // If char token is not null then set prefix to char token’s value. + if (char_token) prefix = char_token->value; + // If prefix is not the empty string and not options’s prefix code point: + if (!prefix.empty() && prefix != options.get_prefix()) { + // Append prefix to the end of parser’s pending fixed value. + parser.pending_fixed_value.append(prefix); + // Set prefix to the empty string. + prefix.clear(); + } + // Run maybe add a part from the pending fixed value given parser. + if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) { + ada_log("maybe_add_part_from_the_pending_fixed_value failed"); + return tl::unexpected(*error); + } + // Let modifier token be the result of running try to consume a modifier + // token given parser. + auto modifier_token = parser.try_consume_modifier_token(); + // Run add a part given parser, prefix, name token, regexp or wildcard + // token, the empty string, and modifier token. + if (auto error = + parser.add_part(prefix, name_token, regexp_or_wildcard_token, "", + modifier_token)) { + ada_log("parser.add_part failed"); + return tl::unexpected(*error); + } + // Continue. + continue; + } + + // Let fixed token be char token. + auto fixed_token = char_token; + // If fixed token is null, then set fixed token to the result of running try + // to consume a token given parser and "escaped-char". + if (!fixed_token) + fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR); + // If fixed token is not null: + if (fixed_token) { + // Append fixed token’s value to parser’s pending fixed value. + parser.pending_fixed_value.append(fixed_token->value); + // Continue. + continue; + } + // Let open token be the result of running try to consume a token given + // parser and "open". + auto open_token = parser.try_consume_token(token_type::OPEN); + // If open token is not null: + if (open_token) { + // Set prefix be the result of running consume text given parser. + auto prefix_ = parser.consume_text(); + // Set name token to the result of running try to consume a token given + // parser and "name". + name_token = parser.try_consume_token(token_type::NAME); + // Set regexp or wildcard token to the result of running try to consume a + // regexp or wildcard token given parser and name token. + regexp_or_wildcard_token = + parser.try_consume_regexp_or_wildcard_token(name_token); + // Let suffix be the result of running consume text given parser. + auto suffix_ = parser.consume_text(); + // Run consume a required token given parser and "close". + if (!parser.consume_required_token(token_type::CLOSE)) { + ada_log("parser.consume_required_token failed"); + return tl::unexpected(errors::type_error); + } + // Set modifier token to the result of running try to consume a modifier + // token given parser. + auto modifier_token = parser.try_consume_modifier_token(); + // Run add a part given parser, prefix, name token, regexp or wildcard + // token, suffix, and modifier token. + if (auto error = + parser.add_part(prefix_, name_token, regexp_or_wildcard_token, + suffix_, modifier_token)) { + return tl::unexpected(*error); + } + // Continue. + continue; + } + // Run maybe add a part from the pending fixed value given parser. + if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) { + ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992"); + return tl::unexpected(*error); + } + // Run consume a required token given parser and "end". + if (!parser.consume_required_token(token_type::END)) { + return tl::unexpected(errors::type_error); + } + } + ada_log("parser.parts size is: ", parser.parts.size()); + // Return parser’s part list. + return parser.parts; +} + +template +bool protocol_component_matches_special_scheme( + url_pattern_component& component) { + auto& regex = component.regexp; + return regex_provider::regex_match("http", regex) || + regex_provider::regex_match("https", regex) || + regex_provider::regex_match("ws", regex) || + regex_provider::regex_match("wss", regex) || + regex_provider::regex_match("ftp", regex); +} + +template +inline std::optional constructor_string_parser< + regex_provider>::compute_protocol_matches_special_scheme_flag() { + ada_log( + "constructor_string_parser::compute_protocol_matches_special_scheme_" + "flag"); + // Let protocol string be the result of running make a component string given + // parser. + auto protocol_string = make_component_string(); + // Let protocol component be the result of compiling a component given + // protocol string, canonicalize a protocol, and default options. + auto protocol_component = url_pattern_component::compile( + protocol_string, canonicalize_protocol, + url_pattern_compile_component_options::DEFAULT); + if (!protocol_component) { + ada_log("url_pattern_component::compile failed for protocol_string ", + protocol_string); + return protocol_component.error(); + } + // If the result of running protocol component matches a special scheme given + // protocol component is true, then set parser’s protocol matches a special + // scheme flag to true. + if (protocol_component_matches_special_scheme(*protocol_component)) { + protocol_matches_a_special_scheme_flag = true; + } + return std::nullopt; +} + +template +tl::expected +constructor_string_parser::parse(std::string_view input) { + ada_log("constructor_string_parser::parse input=", input); + // Let parser be a new constructor string parser whose input is input and + // token list is the result of running tokenize given input and "lenient". + auto token_list = tokenize(input, token_policy::LENIENT); + if (!token_list) { + return tl::unexpected(token_list.error()); + } + auto parser = constructor_string_parser(input, std::move(*token_list)); + + // While parser’s token index is less than parser’s token list size: + while (parser.token_index < parser.token_list.size()) { + // Set parser’s token increment to 1. + parser.token_increment = 1; + + // If parser’s token list[parser’s token index]'s type is "end" then: + if (parser.token_list[parser.token_index].type == token_type::END) { + // If parser’s state is "init": + if (parser.state == State::INIT) { + // Run rewind given parser. + parser.rewind(); + // If the result of running is a hash prefix given parser is true, then + // run change state given parser, "hash" and 1. + if (parser.is_hash_prefix()) { + parser.change_state(State::HASH, 1); + } else if (parser.is_search_prefix()) { + // Otherwise if the result of running is a search prefix given parser + // is true: Run change state given parser, "search" and 1. + parser.change_state(State::SEARCH, 1); + } else { + // Run change state given parser, "pathname" and 0. + parser.change_state(State::PATHNAME, 0); + } + // Increment parser’s token index by parser’s token increment. + parser.token_index += parser.token_increment; + // Continue. + continue; + } + + if (parser.state == State::AUTHORITY) { + // If parser’s state is "authority": + // Run rewind and set state given parser, and "hostname". + parser.rewind(); + parser.change_state(State::HOSTNAME, 0); + // Increment parser’s token index by parser’s token increment. + parser.token_index += parser.token_increment; + // Continue. + continue; + } + + // Run change state given parser, "done" and 0. + parser.change_state(State::DONE, 0); + // Break. + break; + } + + // If the result of running is a group open given parser is true: + if (parser.is_group_open()) { + // Increment parser’s group depth by 1. + parser.group_depth += 1; + // Increment parser’s token index by parser’s token increment. + parser.token_index += parser.token_increment; + } + + // If parser’s group depth is greater than 0: + if (parser.group_depth > 0) { + // If the result of running is a group close given parser is true, then + // decrement parser’s group depth by 1. + if (parser.is_group_close()) { + parser.group_depth -= 1; + } else { + // Increment parser’s token index by parser’s token increment. + parser.token_index += parser.token_increment; + continue; + } + } + + // Switch on parser’s state and run the associated steps: + switch (parser.state) { + case State::INIT: { + // If the result of running is a protocol suffix given parser is true: + if (parser.is_protocol_suffix()) { + // Run rewind and set state given parser and "protocol". + parser.rewind(); + parser.change_state(State::PROTOCOL, 0); + } + break; + } + case State::PROTOCOL: { + // If the result of running is a protocol suffix given parser is true: + if (parser.is_protocol_suffix()) { + // Run compute protocol matches a special scheme flag given parser. + if (const auto error = + parser.compute_protocol_matches_special_scheme_flag()) { + ada_log("compute_protocol_matches_special_scheme_flag failed"); + return tl::unexpected(*error); + } + // Let next state be "pathname". + auto next_state = State::PATHNAME; + // Let skip be 1. + auto skip = 1; + // If the result of running next is authority slashes given parser is + // true: + if (parser.next_is_authority_slashes()) { + // Set next state to "authority". + next_state = State::AUTHORITY; + // Set skip to 3. + skip = 3; + } else if (parser.protocol_matches_a_special_scheme_flag) { + // Otherwise if parser’s protocol matches a special scheme flag is + // true, then set next state to "authority". + next_state = State::AUTHORITY; + } + + // Run change state given parser, next state, and skip. + parser.change_state(next_state, skip); + } + break; + } + case State::AUTHORITY: { + // If the result of running is an identity terminator given parser is + // true, then run rewind and set state given parser and "username". + if (parser.is_an_identity_terminator()) { + parser.rewind(); + parser.change_state(State::USERNAME, 0); + } else if (parser.is_pathname_start() || parser.is_search_prefix() || + parser.is_hash_prefix()) { + // Otherwise if any of the following are true: + // - the result of running is a pathname start given parser; + // - the result of running is a search prefix given parser; or + // - the result of running is a hash prefix given parser, + // then run rewind and set state given parser and "hostname". + parser.rewind(); + parser.change_state(State::HOSTNAME, 0); + } + break; + } + case State::USERNAME: { + // If the result of running is a password prefix given parser is true, + // then run change state given parser, "password", and 1. + if (parser.is_password_prefix()) { + parser.change_state(State::PASSWORD, 1); + } else if (parser.is_an_identity_terminator()) { + // Otherwise if the result of running is an identity terminator given + // parser is true, then run change state given parser, "hostname", + // and 1. + parser.change_state(State::HOSTNAME, 1); + } + break; + } + case State::PASSWORD: { + // If the result of running is an identity terminator given parser is + // true, then run change state given parser, "hostname", and 1. + if (parser.is_an_identity_terminator()) { + parser.change_state(State::HOSTNAME, 1); + } + break; + } + case State::HOSTNAME: { + // If the result of running is an IPv6 open given parser is true, then + // increment parser’s hostname IPv6 bracket depth by 1. + if (parser.is_an_ipv6_open()) { + parser.hostname_ipv6_bracket_depth += 1; + } else if (parser.is_an_ipv6_close()) { + // Otherwise if the result of running is an IPv6 close given parser is + // true, then decrement parser’s hostname IPv6 bracket depth by 1. + parser.hostname_ipv6_bracket_depth -= 1; + } else if (parser.is_port_prefix() && + parser.hostname_ipv6_bracket_depth == 0) { + // Otherwise if the result of running is a port prefix given parser is + // true and parser’s hostname IPv6 bracket depth is zero, then run + // change state given parser, "port", and 1. + parser.change_state(State::PORT, 1); + } else if (parser.is_pathname_start()) { + // Otherwise if the result of running is a pathname start given parser + // is true, then run change state given parser, "pathname", and 0. + parser.change_state(State::PATHNAME, 0); + } else if (parser.is_search_prefix()) { + // Otherwise if the result of running is a search prefix given parser + // is true, then run change state given parser, "search", and 1. + parser.change_state(State::SEARCH, 1); + } else if (parser.is_hash_prefix()) { + // Otherwise if the result of running is a hash prefix given parser is + // true, then run change state given parser, "hash", and 1. + parser.change_state(State::HASH, 1); + } + + break; + } + case State::PORT: { + // If the result of running is a pathname start given parser is true, + // then run change state given parser, "pathname", and 0. + if (parser.is_pathname_start()) { + parser.change_state(State::PATHNAME, 0); + } else if (parser.is_search_prefix()) { + // Otherwise if the result of running is a search prefix given parser + // is true, then run change state given parser, "search", and 1. + parser.change_state(State::SEARCH, 1); + } else if (parser.is_hash_prefix()) { + // Otherwise if the result of running is a hash prefix given parser is + // true, then run change state given parser, "hash", and 1. + parser.change_state(State::HASH, 1); + } + break; + } + case State::PATHNAME: { + // If the result of running is a search prefix given parser is true, + // then run change state given parser, "search", and 1. + if (parser.is_search_prefix()) { + parser.change_state(State::SEARCH, 1); + } else if (parser.is_hash_prefix()) { + // Otherwise if the result of running is a hash prefix given parser is + // true, then run change state given parser, "hash", and 1. + parser.change_state(State::HASH, 1); + } + break; + } + case State::SEARCH: { + // If the result of running is a hash prefix given parser is true, then + // run change state given parser, "hash", and 1. + if (parser.is_hash_prefix()) { + parser.change_state(State::HASH, 1); + } + } + case State::HASH: { + // Do nothing + break; + } + default: { + // Assert: This step is never reached. + unreachable(); + } + } + + // Increment parser’s token index by parser’s token increment. + parser.token_index += parser.token_increment; + } + + // If parser’s result contains "hostname" and not "port", then set parser’s + // result["port"] to the empty string. + if (parser.result.hostname && !parser.result.port) { + parser.result.port = ""; + } + + // Return parser’s result. + return parser.result; } -} // namespace ada +} // namespace ada::url_pattern_helpers -#endif // ADA_URL_SEARCH_PARAMS_INL_H -/* end file include/ada/url_search_params-inl.h */ +#endif +/* end file include/ada/url_pattern_helpers-inl.h */ // Public API /* begin file include/ada/ada_version.h */ @@ -7321,62 +10253,32 @@ enum { #endif // ADA_ADA_VERSION_H /* end file include/ada/ada_version.h */ -/* begin file include/ada/implementation.h */ +/* begin file include/ada/implementation-inl.h */ /** - * @file implementation.h - * @brief Definitions for user facing functions for parsing URL and it's - * components. + * @file implementation-inl.h */ -#ifndef ADA_IMPLEMENTATION_H -#define ADA_IMPLEMENTATION_H +#ifndef ADA_IMPLEMENTATION_INL_H +#define ADA_IMPLEMENTATION_INL_H -#include -#include +#include +#include namespace ada { -enum class errors { generic_error }; - -template -using result = tl::expected; - -/** - * The URL parser takes a scalar value string input, with an optional null or - * base URL base (default null). The parser assumes the input is a valid ASCII - * or UTF-8 string. - * - * @param input the string input to analyze (must be valid ASCII or UTF-8) - * @param base_url the optional URL input to use as a base url. - * @return a parsed URL. - */ -template -ada_warn_unused ada::result parse( - std::string_view input, const result_type* base_url = nullptr); - -extern template ada::result parse(std::string_view input, - const url* base_url); -extern template ada::result parse( - std::string_view input, const url_aggregator* base_url); -/** - * Verifies whether the URL strings can be parsed. The function assumes - * that the inputs are valid ASCII or UTF-8 strings. - * @see https://url.spec.whatwg.org/#dom-url-canparse - * @return If URL can be parsed or not. - */ -bool can_parse(std::string_view input, - const std::string_view* base_input = nullptr); +template +ada_warn_unused tl::expected, errors> +parse_url_pattern(std::variant input, + const std::string_view* base_url, + const url_pattern_options* options) { + return parser::parse_url_pattern_impl(std::move(input), + base_url, options); +} -/** - * Computes a href string from a file path. The function assumes - * that the input is a valid ASCII or UTF-8 string. - * @return a href string (starts with file:://) - */ -std::string href_from_file(std::string_view path); } // namespace ada -#endif // ADA_IMPLEMENTATION_H -/* end file include/ada/implementation.h */ +#endif // ADA_IMPLEMENTATION_INL_H +/* end file include/ada/implementation-inl.h */ #endif // ADA_H /* end file include/ada.h */ diff --git a/lib/internal/bootstrap/web/exposed-window-or-worker.js b/lib/internal/bootstrap/web/exposed-window-or-worker.js index d7d9b60b1c8e94..f0a0cd7132dc48 100644 --- a/lib/internal/bootstrap/web/exposed-window-or-worker.js +++ b/lib/internal/bootstrap/web/exposed-window-or-worker.js @@ -19,6 +19,7 @@ const { defineLazyProperties, defineReplaceableLazyAttribute, exposeLazyInterfaces, + exposeInterface, } = require('internal/util'); const { @@ -26,6 +27,10 @@ const { ERR_NO_CRYPTO, } = require('internal/errors').codes; +const { installObjectURLMethods, URLPattern } = require('internal/url'); +// https://urlpattern.spec.whatwg.org/#urlpattern-class +exposeInterface(globalThis, 'URLPattern', URLPattern); + // https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope const timers = require('timers'); defineOperation(globalThis, 'clearInterval', timers.clearInterval); @@ -63,7 +68,6 @@ exposeLazyInterfaces(globalThis, 'perf_hooks', [ defineReplaceableLazyAttribute(globalThis, 'perf_hooks', ['performance']); // https://w3c.github.io/FileAPI/#creating-revoking -const { installObjectURLMethods } = require('internal/url'); installObjectURLMethods(); let fetchImpl; diff --git a/lib/internal/url.js b/lib/internal/url.js index f6e58e196860fc..90d37813e8b2ed 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -32,6 +32,7 @@ const { decodeURIComponent, } = primordials; +const { URLPattern } = internalBinding('url_pattern'); const { inspect } = require('internal/util/inspect'); const { encodeStr, @@ -1574,6 +1575,7 @@ module.exports = { toPathIfFileURL, installObjectURLMethods, URL, + URLPattern, URLSearchParams, URLParse: URL.parse, domainToASCII, diff --git a/lib/url.js b/lib/url.js index 31299653610002..c23d9c51c20033 100644 --- a/lib/url.js +++ b/lib/url.js @@ -30,6 +30,7 @@ const { decodeURIComponent, } = primordials; +const { URLPattern } = internalBinding('url_pattern'); const { toASCII } = internalBinding('encoding_binding'); const { encodeStr, hexTable } = require('internal/querystring'); const querystring = require('querystring'); @@ -1029,6 +1030,7 @@ module.exports = { // WHATWG API URL, + URLPattern, URLSearchParams, domainToASCII, domainToUnicode, diff --git a/node.gyp b/node.gyp index 1633ed2d832fc5..473c86b1504aee 100644 --- a/node.gyp +++ b/node.gyp @@ -146,6 +146,7 @@ 'src/node_trace_events.cc', 'src/node_types.cc', 'src/node_url.cc', + 'src/node_url_pattern.cc', 'src/node_util.cc', 'src/node_v8.cc', 'src/node_wasi.cc', @@ -275,6 +276,7 @@ 'src/node_stat_watcher.h', 'src/node_union_bytes.h', 'src/node_url.h', + 'src/node_url_pattern.h', 'src/node_version.h', 'src/node_v8.h', 'src/node_v8_platform-inl.h', diff --git a/src/env_properties.h b/src/env_properties.h index d9e18cbc4515ac..04fe8918dddbc1 100644 --- a/src/env_properties.h +++ b/src/env_properties.h @@ -78,6 +78,7 @@ V(async_ids_stack_string, "async_ids_stack") \ V(attributes_string, "attributes") \ V(base_string, "base") \ + V(base_url_string, "baseURL") \ V(bits_string, "bits") \ V(block_list_string, "blockList") \ V(buffer_string, "buffer") \ @@ -178,6 +179,9 @@ V(get_data_clone_error_string, "_getDataCloneError") \ V(get_shared_array_buffer_id_string, "_getSharedArrayBufferId") \ V(gid_string, "gid") \ + V(groups_string, "groups") \ + V(has_regexp_groups_string, "hasRegExpGroups") \ + V(hash_string, "hash") \ V(h2_string, "h2") \ V(handle_string, "handle") \ V(hash_algorithm_string, "hashAlgorithm") \ @@ -185,6 +189,7 @@ V(homedir_string, "homedir") \ V(host_string, "host") \ V(hostmaster_string, "hostmaster") \ + V(hostname_string, "hostname") \ V(http_1_1_string, "http/1.1") \ V(id_string, "id") \ V(identity_string, "identity") \ @@ -192,6 +197,7 @@ V(infoaccess_string, "infoAccess") \ V(inherit_string, "inherit") \ V(input_string, "input") \ + V(inputs_string, "inputs") \ V(internal_binding_string, "internalBinding") \ V(internal_string, "internal") \ V(ipv4_string, "IPv4") \ @@ -279,6 +285,7 @@ V(parse_error_string, "Parse Error") \ V(password_string, "password") \ V(path_string, "path") \ + V(pathname_string, "pathname") \ V(pending_handle_string, "pendingHandle") \ V(permission_string, "permission") \ V(pid_string, "pid") \ @@ -294,6 +301,7 @@ V(priority_string, "priority") \ V(process_string, "process") \ V(promise_string, "promise") \ + V(protocol_string, "protocol") \ V(prototype_string, "prototype") \ V(psk_string, "psk") \ V(pubkey_string, "pubkey") \ @@ -321,6 +329,7 @@ V(scheme_string, "scheme") \ V(scopeid_string, "scopeid") \ V(script_name_string, "scriptName") \ + V(search_string, "search") \ V(serial_number_string, "serialNumber") \ V(serial_string, "serial") \ V(servername_string, "servername") \ diff --git a/src/node_binding.cc b/src/node_binding.cc index c2ef9b36d5b296..e93f3312cc6425 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -4,6 +4,7 @@ #include "node_builtins.h" #include "node_errors.h" #include "node_external_reference.h" +#include "node_url_pattern.h" #include "util.h" #include @@ -87,6 +88,7 @@ V(types) \ V(udp_wrap) \ V(url) \ + V(url_pattern) \ V(util) \ V(uv) \ V(v8) \ diff --git a/src/node_external_reference.h b/src/node_external_reference.h index 8d49a119c21832..3976a0e9fb23d4 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -180,6 +180,7 @@ class ExternalReferenceRegistry { V(tty_wrap) \ V(udp_wrap) \ V(url) \ + V(url_pattern) \ V(util) \ V(pipe_wrap) \ V(sea) \ diff --git a/src/node_url_pattern.cc b/src/node_url_pattern.cc new file mode 100644 index 00000000000000..78af5b85e3e3b2 --- /dev/null +++ b/src/node_url_pattern.cc @@ -0,0 +1,708 @@ +#include "node_url_pattern.h" +#include "base_object-inl.h" +#include "debug_utils-inl.h" +#include "env-inl.h" +#include "memory_tracker-inl.h" +#include "node.h" +#include "node_errors.h" +#include "node_mem-inl.h" +#include "path.h" +#include "util-inl.h" + +namespace node::url_pattern { + +using v8::Array; +using v8::Context; +using v8::DontDelete; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Isolate; +using v8::Local; +using v8::LocalVector; +using v8::MaybeLocal; +using v8::Name; +using v8::Object; +using v8::PropertyAttribute; +using v8::ReadOnly; +using v8::RegExp; +using v8::String; +using v8::Value; + +std::optional +URLPatternRegexProvider::create_instance(std::string_view pattern, + bool ignore_case) { + auto isolate = Isolate::GetCurrent(); + auto env = Environment::GetCurrent(isolate); + int flags = RegExp::Flags::kUnicode; + if (ignore_case) { + flags |= static_cast(RegExp::Flags::kIgnoreCase); + } + + Local local_pattern; + if (!String::NewFromUtf8( + isolate, pattern.data(), v8::NewStringType::kNormal, pattern.size()) + .ToLocal(&local_pattern)) { + return std::nullopt; + } + Local regexp; + if (!RegExp::New( + env->context(), local_pattern, static_cast(flags)) + .ToLocal(®exp)) { + return std::nullopt; + } + return v8::Global(isolate, regexp); +} + +bool URLPatternRegexProvider::regex_match(std::string_view input, + const regex_type& pattern) { + auto isolate = Isolate::GetCurrent(); + auto env = Environment::GetCurrent(isolate); + Local local_input; + if (!String::NewFromUtf8( + isolate, input.data(), v8::NewStringType::kNormal, input.size()) + .ToLocal(&local_input)) { + return false; + } + Local result_object; + if (!pattern.Get(isolate)->Exec(env->context(), local_input).ToLocal(&result_object) || + result_object->IsNull()) { + return false; + } + CHECK(result_object->IsArray()); + return result_object.As()->Length() > 0; +} + +std::optional> URLPatternRegexProvider::regex_search( + std::string_view input, const regex_type& global_pattern) { + auto isolate = Isolate::GetCurrent(); + auto env = Environment::GetCurrent(isolate); + Local local_input; + if (!String::NewFromUtf8( + isolate, input.data(), v8::NewStringType::kNormal, input.size()) + .ToLocal(&local_input)) { + return std::nullopt; + } + Local exec_result_object; + auto pattern = global_pattern.Get(isolate); + if (!pattern->Exec(env->context(), local_input) + .ToLocal(&exec_result_object) || + exec_result_object->IsNull()) { + return std::nullopt; + } + CHECK(exec_result_object->IsArray()); + auto exec_result = exec_result_object.As(); + std::vector result; + result.reserve(exec_result->Length()); + for (size_t i = 0; i < result.capacity(); i++) { + Local entry; + if (!exec_result->Get(env->context(), i).ToLocal(&entry) || !entry->IsString()) { + return std::nullopt; + } + Utf8Value utf8_entry(isolate, entry.As()); + result.emplace_back(utf8_entry.ToString()); + } + return result; +} + +URLPattern::URLPattern(Environment* env, + Local object, + ada::url_pattern&& url_pattern) + : BaseObject(env, object), url_pattern_(std::move(url_pattern)) { + MakeWeak(); +} + +void URLPattern::MemoryInfo(MemoryTracker* tracker) const { + // TODO(anonrig): Implement this. +} + +void URLPattern::New(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args.IsConstructCall()); + + // If no arguments are passed, then we parse the empty URLPattern. + if (args.Length() == 0) { + auto init = ada::url_pattern_init{}; + auto url_pattern = ada::parse_url_pattern(init); + CHECK(url_pattern); + new URLPattern(env, args.This(), std::move(*url_pattern)); + return; + } + + std::optional init{}; + std::optional input{}; + std::optional base_url{}; + std::optional options{}; + + // Following patterns are supported: + // - new URLPattern(input) + // - new URLPattern(input, baseURL) + // - new URLPattern(input, options) + // - new URLPattern(input, baseURL, options) + if (args[0]->IsString()) { + BufferValue input_buffer(env->isolate(), args[0]); + CHECK_NOT_NULL(*input_buffer); + input = input_buffer.ToString(); + } else if (args[0]->IsObject()) { + init = URLPatternInit::FromJsObject(env, args[0].As()); + } else { + env->ThrowTypeError("input must be an object or a string."); + return; + } + + // The next argument can be baseURL or options. + if (args.Length() > 1) { + if (args[1]->IsString()) { + BufferValue base_url_buffer(env->isolate(), args[1]); + CHECK_NOT_NULL(*base_url_buffer); + base_url = base_url_buffer.ToString(); + } else if (args[1]->IsObject()) { + CHECK(!options.has_value()); + options = URLPatternOptions::FromJsObject(env, args[0].As()); + } else { + env->ThrowTypeError("baseURL or options must be provided"); + return; + } + + // Only remaining argument can be options. + if (args.Length() > 2) { + if (!args[2]->IsObject()) { + THROW_ERR_INVALID_ARG_TYPE(env, "options must be an object"); + return; + } + CHECK(!options.has_value()); + options = URLPatternOptions::FromJsObject(env, args[2].As()); + } + } + + // Either url_pattern_init or input as a string must be provided. + CHECK_IMPLIES(init.has_value(), !input.has_value()); + + std::string_view base_url_view{}; + if (base_url) base_url_view = {base_url->data(), base_url->size()}; + + tl::expected, ada::errors> + url_pattern = init.has_value() + ? parse_url_pattern( + *init, + base_url ? &base_url_view : nullptr, + options.has_value() ? &options.value() : nullptr) + : parse_url_pattern( + *input, + base_url ? &base_url_view : nullptr, + options.has_value() ? &options.value() : nullptr); + + if (!url_pattern) { + env->ThrowTypeError("Failed to construct URLPattern"); + return; + } + + new URLPattern(env, args.This(), std::move(*url_pattern)); +} + +MaybeLocal URLPattern::URLPatternInit::ToJsObject( + Environment* env, const ada::url_pattern_init& init) { + auto isolate = env->isolate(); + auto context = env->context(); + auto result = Object::New(isolate); + if (init.protocol) { + USE(result->Set(context, + env->protocol_string(), + ToV8Value(context, *init.protocol).ToLocalChecked())); + } + if (init.username) { + USE(result->Set(context, + env->username_string(), + ToV8Value(context, *init.username).ToLocalChecked())); + } + if (init.password) { + USE(result->Set(context, + env->password_string(), + ToV8Value(context, *init.password).ToLocalChecked())); + } + if (init.hostname) { + USE(result->Set(context, + env->hostname_string(), + ToV8Value(context, *init.hostname).ToLocalChecked())); + } + if (init.port) { + USE(result->Set(context, + env->port_string(), + ToV8Value(context, *init.port).ToLocalChecked())); + } + if (init.pathname) { + USE(result->Set(context, + env->pathname_string(), + ToV8Value(context, *init.pathname).ToLocalChecked())); + } + if (init.search) { + USE(result->Set(context, + env->search_string(), + ToV8Value(context, *init.search).ToLocalChecked())); + } + if (init.hash) { + USE(result->Set(context, + env->hash_string(), + ToV8Value(context, *init.hash).ToLocalChecked())); + } + if (init.base_url) { + USE(result->Set(context, + env->base_url_string(), + ToV8Value(context, *init.base_url).ToLocalChecked())); + } + return result; +} + +ada::url_pattern_init URLPattern::URLPatternInit::FromJsObject( + Environment* env, Local obj) { + ada::url_pattern_init init{}; + Local components[] = { + env->protocol_string(), + env->username_string(), + env->password_string(), + env->hostname_string(), + env->port_string(), + env->pathname_string(), + env->search_string(), + env->hash_string(), + env->base_url_string(), + }; + Local value; + auto isolate = env->isolate(); + auto set_parameter = [&](Local component, + std::string_view utf8_value) { + // TODO(anonrig): Optimization opportunity. + // Get rid of calling env->xxx_string() for every component match. + if (component == env->protocol_string()) { + init.protocol = std::string(utf8_value); + } else if (component == env->username_string()) { + init.username = std::string(utf8_value); + } else if (component == env->password_string()) { + init.password = std::string(utf8_value); + } else if (component == env->hostname_string()) { + init.hostname = std::string(utf8_value); + } else if (component == env->port_string()) { + init.port = std::string(utf8_value); + } else if (component == env->pathname_string()) { + init.pathname = std::string(utf8_value); + } else if (component == env->search_string()) { + init.search = std::string(utf8_value); + } else if (component == env->hash_string()) { + init.hash = std::string(utf8_value); + } else if (component == env->base_url_string()) { + init.base_url = std::string(utf8_value); + } + }; + for (const auto& component : components) { + if (obj->Get(env->context(), component).ToLocal(&value)) { + if (value->IsString()) { + Utf8Value utf8_value(isolate, value); + set_parameter(component, utf8_value.ToStringView()); + } + } + } + return init; +} + +Local URLPattern::URLPatternComponentResult::ToJSObject( + Environment* env, const ada::url_pattern_component_result& result) { + auto isolate = env->isolate(); + auto context = env->context(); + const auto parse_groups = + [&](std::unordered_map groups) { + // TODO(@anonrig): Use predefined object constructor to avoid calling + // Set on each key. + auto res = Object::New(isolate); + for (const auto& group : groups) { + USE(res->Set(context, + ToV8Value(context, group.first).ToLocalChecked(), + ToV8Value(context, group.second).ToLocalChecked())); + } + return res; + }; + Local names[] = {env->input_string(), env->groups_string()}; + Local values[] = { + ToV8Value(env->context(), result.input).ToLocalChecked(), + parse_groups(result.groups), + }; + DCHECK_EQ(arraysize(names), arraysize(values)); + return Object::New( + isolate, Object::New(isolate), names, values, arraysize(names)); +} + +Local URLPattern::URLPatternResult::ToJSObject( + Environment* env, const ada::url_pattern_result& result) { + auto isolate = env->isolate(); + Local names[] = { + env->inputs_string(), + env->protocol_string(), + env->username_string(), + env->password_string(), + env->hostname_string(), + env->port_string(), + env->pathname_string(), + env->search_string(), + env->hash_string(), + }; + LocalVector inputs(isolate, result.inputs.size()); + size_t index = 0; + for (auto& input : result.inputs) { + if (std::holds_alternative(input)) { + auto input_str = std::get(input); + inputs[index] = ToV8Value(env->context(), input_str).ToLocalChecked(); + } else { + DCHECK(std::holds_alternative(input)); + auto init = std::get(input); + inputs[index] = URLPatternInit::ToJsObject(env, init).ToLocalChecked(); + } + index++; + } + Local values[] = { + Array::New(isolate, inputs.data(), inputs.size()), + URLPatternComponentResult::ToJSObject(env, result.protocol), + URLPatternComponentResult::ToJSObject(env, result.username), + URLPatternComponentResult::ToJSObject(env, result.password), + URLPatternComponentResult::ToJSObject(env, result.hostname), + URLPatternComponentResult::ToJSObject(env, result.port), + URLPatternComponentResult::ToJSObject(env, result.pathname), + URLPatternComponentResult::ToJSObject(env, result.search), + URLPatternComponentResult::ToJSObject(env, result.hash), + }; + DCHECK_EQ(arraysize(names), arraysize(values)); + return Object::New( + isolate, Object::New(isolate), names, values, arraysize(names)); +} + +ada::url_pattern_options URLPattern::URLPatternOptions::FromJsObject( + Environment* env, Local obj) { + ada::url_pattern_options options{}; + Local ignore_case; + if (obj->Get(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "ignoreCase")) + .ToLocal(&ignore_case)) { + options.ignore_case = ignore_case->IsTrue(); + } + return options; +} + +MaybeLocal URLPattern::Hash() const { + auto context = env()->context(); + return ToV8Value(context, url_pattern_.get_hash()); +} + +MaybeLocal URLPattern::Hostname() const { + auto context = env()->context(); + return ToV8Value(context, url_pattern_.get_hostname()); +} + +MaybeLocal URLPattern::Password() const { + auto context = env()->context(); + return ToV8Value(context, url_pattern_.get_password()); +} + +MaybeLocal URLPattern::Pathname() const { + auto context = env()->context(); + return ToV8Value(context, url_pattern_.get_pathname()); +} + +MaybeLocal URLPattern::Port() const { + auto context = env()->context(); + return ToV8Value(context, url_pattern_.get_port()); +} + +MaybeLocal URLPattern::Protocol() const { + auto context = env()->context(); + return ToV8Value(context, url_pattern_.get_protocol()); +} + +MaybeLocal URLPattern::Search() const { + auto context = env()->context(); + return ToV8Value(context, url_pattern_.get_search()); +} + +MaybeLocal URLPattern::Username() const { + auto context = env()->context(); + return ToV8Value(context, url_pattern_.get_username()); +} + +bool URLPattern::HasRegExpGroups() const { + return url_pattern_.has_regexp_groups(); +} + +// Instance methods + +MaybeLocal URLPattern::Exec(Environment* env, + const ada::url_pattern_input& input, + std::optional& baseURL) { + if (auto result = + url_pattern_.exec(input, baseURL ? &baseURL.value() : nullptr)) { + if (result->has_value()) { + return URLPatternResult::ToJSObject(env, result->value()); + } + return Null(env->isolate()); + } + env->ThrowTypeError("Failed to exec URLPattern"); + return {}; +} + +bool URLPattern::Test(Environment* env, + const ada::url_pattern_input& input, + std::optional& baseURL) { + if (auto result = + url_pattern_.test(input, baseURL ? &baseURL.value() : nullptr)) { + return *result; + } + env->ThrowTypeError("Failed to test URLPattern"); + return false; +} + +// V8 Methods + +void URLPattern::Exec(const FunctionCallbackInfo& args) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, args.This()); + if (args.Length() == 0) { // input, [baseURL] + return args.GetReturnValue().SetNull(); + } + auto env = Environment::GetCurrent(args); + + ada::url_pattern_input input; + std::optional baseURL{}; + std::string input_base; + if (args[0]->IsString()) { + Utf8Value input_value(env->isolate(), args[0].As()); + input_base = input_value.ToString(); + input = std::string_view(input_base); + } else if (args[0]->IsObject()) { + input = URLPatternInit::FromJsObject(env, args[0].As()); + } else { + THROW_ERR_INVALID_ARG_TYPE( + env, "URLPattern input needs to be a string or an object"); + return; + } + + if (args.Length() > 1 && args[1]->IsString()) { + Utf8Value base_url_value(env->isolate(), args[1].As()); + baseURL = base_url_value.ToStringView(); + } + + Local result; + std::optional baseURL_opt = + baseURL ? std::optional(*baseURL) : std::nullopt; + if (!url_pattern->Exec(env, input, baseURL_opt).ToLocal(&result)) { + return; + } + args.GetReturnValue().Set(result); +} + +void URLPattern::Test(const FunctionCallbackInfo& args) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, args.This()); + if (args.Length() == 0) { // input, [baseURL] + return args.GetReturnValue().Set(false); + } + auto env = Environment::GetCurrent(args); + + ada::url_pattern_input input; + std::optional baseURL{}; + std::string input_base; + if (args[0]->IsString()) { + Utf8Value input_value(env->isolate(), args[0].As()); + input_base = input_value.ToString(); + input = std::string_view(input_base); + } else if (args[0]->IsObject()) { + input = URLPatternInit::FromJsObject(env, args[0].As()); + } else { + THROW_ERR_INVALID_ARG_TYPE( + env, "URLPattern input needs to be a string or an object"); + return; + } + + if (args.Length() > 1 && args[1]->IsString()) { + Utf8Value base_url_value(env->isolate(), args[1].As()); + baseURL = base_url_value.ToStringView(); + } + + std::optional baseURL_opt = + baseURL ? std::optional(*baseURL) : std::nullopt; + args.GetReturnValue().Set(url_pattern->Test(env, input, baseURL_opt)); +} + +void URLPattern::Protocol(const FunctionCallbackInfo& info) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); + Local result; + if (!url_pattern->Protocol().ToLocal(&result)) { + return; + } + info.GetReturnValue().Set(result); +} + +void URLPattern::Username(const FunctionCallbackInfo& info) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); + Local result; + if (!url_pattern->Username().ToLocal(&result)) { + return; + } + info.GetReturnValue().Set(result); +} + +void URLPattern::Password(const FunctionCallbackInfo& info) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); + Local result; + if (!url_pattern->Password().ToLocal(&result)) { + return; + } + info.GetReturnValue().Set(result); +} + +void URLPattern::Hostname(const FunctionCallbackInfo& info) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); + Local result; + if (!url_pattern->Hostname().ToLocal(&result)) { + return; + } + info.GetReturnValue().Set(result); +} + +void URLPattern::Port(const FunctionCallbackInfo& info) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); + Local result; + if (!url_pattern->Port().ToLocal(&result)) { + return; + } + info.GetReturnValue().Set(result); +} + +void URLPattern::Pathname(const FunctionCallbackInfo& info) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); + Local result; + if (!url_pattern->Pathname().ToLocal(&result)) { + return; + } + info.GetReturnValue().Set(result); +} + +void URLPattern::Search(const FunctionCallbackInfo& info) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); + Local result; + if (!url_pattern->Search().ToLocal(&result)) { + return; + } + info.GetReturnValue().Set(result); +} + +void URLPattern::Hash(const FunctionCallbackInfo& info) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); + Local result; + if (!url_pattern->Hash().ToLocal(&result)) { + return; + } + info.GetReturnValue().Set(result); +} + +void URLPattern::HasRegexpGroups(const FunctionCallbackInfo& info) { + URLPattern* url_pattern; + ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); + info.GetReturnValue().Set(url_pattern->HasRegExpGroups()); +} + +static void RegisterExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(URLPattern::New); + registry->Register(URLPattern::Protocol); + registry->Register(URLPattern::Username); + registry->Register(URLPattern::Password); + registry->Register(URLPattern::Hostname); + registry->Register(URLPattern::Port); + registry->Register(URLPattern::Pathname); + registry->Register(URLPattern::Search); + registry->Register(URLPattern::Hash); + registry->Register(URLPattern::HasRegexpGroups); + registry->Register(URLPattern::Exec); + registry->Register(URLPattern::Test); +} + +static void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + auto attributes = static_cast(ReadOnly | DontDelete); + auto ctor_tmpl = NewFunctionTemplate(isolate, URLPattern::New); + auto instance_template = ctor_tmpl->InstanceTemplate(); + auto prototype_template = ctor_tmpl->PrototypeTemplate(); + ctor_tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "URLPattern")); + + instance_template->SetInternalFieldCount(URLPattern::kInternalFieldCount); + prototype_template->SetAccessorProperty( + env->protocol_string(), + FunctionTemplate::New(isolate, URLPattern::Protocol), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->username_string(), + FunctionTemplate::New(isolate, URLPattern::Username), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->password_string(), + FunctionTemplate::New(isolate, URLPattern::Password), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->hostname_string(), + FunctionTemplate::New(isolate, URLPattern::Hostname), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->port_string(), + FunctionTemplate::New(isolate, URLPattern::Port), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->pathname_string(), + FunctionTemplate::New(isolate, URLPattern::Pathname), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->search_string(), + FunctionTemplate::New(isolate, URLPattern::Search), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->hash_string(), + FunctionTemplate::New(isolate, URLPattern::Hash), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->has_regexp_groups_string(), + FunctionTemplate::New(isolate, URLPattern::HasRegexpGroups), + Local(), + attributes); + + SetProtoMethodNoSideEffect(isolate, ctor_tmpl, "exec", URLPattern::Exec); + SetProtoMethodNoSideEffect(isolate, ctor_tmpl, "test", URLPattern::Test); + SetConstructorFunction(context, target, "URLPattern", ctor_tmpl); +} + +} // namespace node::url_pattern + +NODE_BINDING_CONTEXT_AWARE_INTERNAL(url_pattern, node::url_pattern::Initialize) +NODE_BINDING_EXTERNAL_REFERENCE(url_pattern, + node::url_pattern::RegisterExternalReferences) diff --git a/src/node_url_pattern.h b/src/node_url_pattern.h new file mode 100644 index 00000000000000..5059be0907a46f --- /dev/null +++ b/src/node_url_pattern.h @@ -0,0 +1,104 @@ +#ifndef SRC_NODE_URL_PATTERN_H_ +#define SRC_NODE_URL_PATTERN_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "ada.h" +#include "base_object.h" +#include "util.h" + +#include + +#include +#include + +namespace node::url_pattern { + +class URLPatternRegexProvider { + public: + using regex_type = v8::Global; + static std::optional create_instance(std::string_view pattern, + bool ignore_case); + static std::optional> regex_search( + std::string_view input, const regex_type& pattern); + static bool regex_match(std::string_view input, const regex_type& pattern); +}; + +class URLPattern : public BaseObject { + public: + URLPattern(Environment* env, + v8::Local object, + ada::url_pattern&& url_pattern); + + void MemoryInfo(MemoryTracker* tracker) const override; + static void New(const v8::FunctionCallbackInfo& args); + + // V8 APIs + static void Exec(const v8::FunctionCallbackInfo& info); + static void Test(const v8::FunctionCallbackInfo& info); + + static void Hash(const v8::FunctionCallbackInfo& info); + static void Hostname(const v8::FunctionCallbackInfo& info); + static void Password(const v8::FunctionCallbackInfo& info); + static void Pathname(const v8::FunctionCallbackInfo& info); + static void Port(const v8::FunctionCallbackInfo& info); + static void Protocol(const v8::FunctionCallbackInfo& info); + static void Search(const v8::FunctionCallbackInfo& info); + static void Username(const v8::FunctionCallbackInfo& info); + static void HasRegexpGroups(const v8::FunctionCallbackInfo& info); + + SET_MEMORY_INFO_NAME(URLPattern) + SET_SELF_SIZE(URLPattern) + + class URLPatternInit { + public: + static ada::url_pattern_init FromJsObject(Environment* env, + v8::Local obj); + static v8::MaybeLocal ToJsObject( + Environment* env, const ada::url_pattern_init& init); + }; + + class URLPatternOptions { + public: + static ada::url_pattern_options FromJsObject(Environment* env, + v8::Local obj); + }; + + class URLPatternResult { + public: + static v8::Local ToJSObject( + Environment* env, const ada::url_pattern_result& result); + }; + + class URLPatternComponentResult { + public: + static v8::Local ToJSObject( + Environment* env, const ada::url_pattern_component_result& result); + }; + + private: + ada::url_pattern url_pattern_; + // Getter methods + v8::MaybeLocal Hash() const; + v8::MaybeLocal Hostname() const; + v8::MaybeLocal Password() const; + v8::MaybeLocal Pathname() const; + v8::MaybeLocal Port() const; + v8::MaybeLocal Protocol() const; + v8::MaybeLocal Search() const; + v8::MaybeLocal Username() const; + bool HasRegExpGroups() const; + v8::MaybeLocal Exec( + Environment* env, + const ada::url_pattern_input& input, + std::optional& baseURL); // NOLINT (runtime/references) + bool Test( + Environment* env, + const ada::url_pattern_input& input, + std::optional& baseURL); // NOLINT (runtime/references) +}; + +} // namespace node::url_pattern + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // SRC_NODE_URL_PATTERN_H_ diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index cc97c8787a2c98..67955f0ae6a07e 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -29,6 +29,7 @@ Last update: - resources: https://github.com/web-platform-tests/wpt/tree/1e140d63ec/resources - streams: https://github.com/web-platform-tests/wpt/tree/bc9dcbbf1a/streams - url: https://github.com/web-platform-tests/wpt/tree/6fa3fe8a92/url +- urlpattern: https://github.com/web-platform-tests/wpt/tree/1b56d89a26/urlpattern - user-timing: https://github.com/web-platform-tests/wpt/tree/5ae85bf826/user-timing - wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi - wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi diff --git a/test/fixtures/wpt/urlpattern/WEB_FEATURES.yml b/test/fixtures/wpt/urlpattern/WEB_FEATURES.yml new file mode 100644 index 00000000000000..cb82ba5cda2918 --- /dev/null +++ b/test/fixtures/wpt/urlpattern/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: urlpattern + files: "**" diff --git a/test/fixtures/wpt/urlpattern/resources/urlpattern-compare-test-data.json b/test/fixtures/wpt/urlpattern/resources/urlpattern-compare-test-data.json new file mode 100644 index 00000000000000..0043ac08bca554 --- /dev/null +++ b/test/fixtures/wpt/urlpattern/resources/urlpattern-compare-test-data.json @@ -0,0 +1,152 @@ +[ + { + "component": "pathname", + "left": { "pathname": "/foo/a" }, + "right": { "pathname": "/foo/b" }, + "expected": -1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/b" }, + "right": { "pathname": "/foo/bar" }, + "expected": -1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/bar" }, + "right": { "pathname": "/foo/:bar" }, + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/" }, + "right": { "pathname": "/foo/:bar" }, + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/:bar" }, + "right": { "pathname": "/foo/*" }, + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/{bar}" }, + "right": { "pathname": "/foo/(bar)" }, + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/{bar}" }, + "right": { "pathname": "/foo/{bar}+" }, + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/{bar}+" }, + "right": { "pathname": "/foo/{bar}?" }, + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/{bar}?" }, + "right": { "pathname": "/foo/{bar}*" }, + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/(123)" }, + "right": { "pathname": "/foo/(12)" }, + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/:b" }, + "right": { "pathname": "/foo/:a" }, + "expected": 0 + }, + { + "component": "pathname", + "left": { "pathname": "*/foo" }, + "right": { "pathname": "*" }, + "expected": 1 + }, + { + "component": "port", + "left": { "port": "9" }, + "right": { "port": "100" }, + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "foo/:bar?/baz" }, + "right": { "pathname": "foo/{:bar}?/baz" }, + "expected": -1 + }, + { + "component": "pathname", + "left": { "pathname": "foo/:bar?/baz" }, + "right": { "pathname": "foo{/:bar}?/baz" }, + "expected": 0 + }, + { + "component": "pathname", + "left": { "pathname": "foo/:bar?/baz" }, + "right": { "pathname": "fo{o/:bar}?/baz" }, + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "foo/:bar?/baz" }, + "right": { "pathname": "foo{/:bar/}?baz" }, + "expected": -1 + }, + { + "component": "pathname", + "left": "https://a.example.com/b?a", + "right": "https://b.example.com/a?b", + "expected": 1 + }, + { + "component": "pathname", + "left": { "pathname": "/foo/{bar}/baz" }, + "right": { "pathname": "/foo/bar/baz" }, + "expected": 0 + }, + { + "component": "protocol", + "left": { "protocol": "a" }, + "right": { "protocol": "b" }, + "expected": -1 + }, + { + "component": "username", + "left": { "username": "a" }, + "right": { "username": "b" }, + "expected": -1 + }, + { + "component": "password", + "left": { "password": "a" }, + "right": { "password": "b" }, + "expected": -1 + }, + { + "component": "hostname", + "left": { "hostname": "a" }, + "right": { "hostname": "b" }, + "expected": -1 + }, + { + "component": "search", + "left": { "search": "a" }, + "right": { "search": "b" }, + "expected": -1 + }, + { + "component": "hash", + "left": { "hash": "a" }, + "right": { "hash": "b" }, + "expected": -1 + } +] diff --git a/test/fixtures/wpt/urlpattern/resources/urlpattern-compare-tests.tentative.js b/test/fixtures/wpt/urlpattern/resources/urlpattern-compare-tests.tentative.js new file mode 100644 index 00000000000000..b92b53ee45cd98 --- /dev/null +++ b/test/fixtures/wpt/urlpattern/resources/urlpattern-compare-tests.tentative.js @@ -0,0 +1,26 @@ +function runTests(data) { + for (let entry of data) { + test(function() { + const left = new URLPattern(entry.left); + const right = new URLPattern(entry.right); + + assert_equals(URLPattern.compareComponent(entry.component, left, right), entry.expected); + + // We have to coerce to an integer here in order to avoid asserting + // that `+0` is `-0`. + const reverse_expected = ~~(entry.expected * -1); + assert_equals(URLPattern.compareComponent(entry.component, right, left), reverse_expected, "reverse order"); + + assert_equals(URLPattern.compareComponent(entry.component, left, left), 0, "left equality"); + assert_equals(URLPattern.compareComponent(entry.component, right, right), 0, "right equality"); + }, `Component: ${entry.component} ` + + `Left: ${JSON.stringify(entry.left)} ` + + `Right: ${JSON.stringify(entry.right)}`); + } +} + +promise_test(async function() { + const response = await fetch('resources/urlpattern-compare-test-data.json'); + const data = await response.json(); + runTests(data); +}, 'Loading data...'); diff --git a/test/fixtures/wpt/urlpattern/resources/urlpattern-hasregexpgroups-tests.js b/test/fixtures/wpt/urlpattern/resources/urlpattern-hasregexpgroups-tests.js new file mode 100644 index 00000000000000..4be886e4a5390d --- /dev/null +++ b/test/fixtures/wpt/urlpattern/resources/urlpattern-hasregexpgroups-tests.js @@ -0,0 +1,18 @@ +test(() => { + assert_implements('hasRegExpGroups' in URLPattern.prototype, "hasRegExpGroups is not implemented"); + assert_false(new URLPattern({}).hasRegExpGroups, "match-everything pattern"); + for (let component of ['protocol', 'username', 'password', 'hostname', 'port', 'pathname', 'search', 'hash']) { + assert_false(new URLPattern({[component]: '*'}).hasRegExpGroups, `wildcard in ${component}`); + assert_false(new URLPattern({[component]: ':foo'}).hasRegExpGroups, `segment wildcard in ${component}`); + assert_false(new URLPattern({[component]: ':foo?'}).hasRegExpGroups, `optional segment wildcard in ${component}`); + assert_true(new URLPattern({[component]: ':foo(hi)'}).hasRegExpGroups, `named regexp group in ${component}`); + assert_true(new URLPattern({[component]: '(hi)'}).hasRegExpGroups, `anonymous regexp group in ${component}`); + if (component !== 'protocol' && component !== 'port') { + // These components are more narrow in what they accept in any case. + assert_false(new URLPattern({[component]: 'a-{:hello}-z-*-a'}).hasRegExpGroups, `wildcards mixed in with fixed text and wildcards in ${component}`); + assert_true(new URLPattern({[component]: 'a-(hi)-z-(lo)-a'}).hasRegExpGroups, `regexp groups mixed in with fixed text and wildcards in ${component}`); + } + } + assert_false(new URLPattern({pathname: '/a/:foo/:baz?/b/*'}).hasRegExpGroups, "complex pathname with no regexp"); + assert_true(new URLPattern({pathname: '/a/:foo/:baz([a-z]+)?/b/*'}).hasRegExpGroups, "complex pathname with regexp"); +}, ''); diff --git a/test/fixtures/wpt/urlpattern/resources/urlpatterntestdata.json b/test/fixtures/wpt/urlpattern/resources/urlpatterntestdata.json new file mode 100644 index 00000000000000..058079bb6d17ac --- /dev/null +++ b/test/fixtures/wpt/urlpattern/resources/urlpatterntestdata.json @@ -0,0 +1,2857 @@ +[ + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "pathname": "/foo/ba" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "pathname": "/foo/bar/" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [ "https://example.com/foo/bar" ], + "expected_match": { + "hostname": { "input": "example.com", "groups": { "0": "example.com" } }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": { "0": "https" } } + } + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [ "https://example.com/foo/bar/baz" ], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "hostname": "example.com", "pathname": "/foo/bar" }], + "expected_match": { + "hostname": { "input": "example.com", "groups": { "0": "example.com" } }, + "pathname": { "input": "/foo/bar", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "hostname": "example.com", "pathname": "/foo/bar/baz" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "pathname": "/foo/bar", "baseURL": "https://example.com" }], + "expected_match": { + "hostname": { "input": "example.com", "groups": { "0": "example.com" } }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": { "0": "https" } } + } + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "pathname": "/foo/bar/baz", + "baseURL": "https://example.com" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [{ "hostname": "example.com", "pathname": "/foo/bar" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [{ "protocol": "https", "hostname": "example.com", + "pathname": "/foo/bar" }], + "exactly_empty_components": [ "port" ], + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com" }], + "inputs": [{ "protocol": "https", "hostname": "example.com", + "pathname": "/foo/bar" }], + "exactly_empty_components": [ "port" ], + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com" }], + "inputs": [{ "protocol": "https", "hostname": "example.com", + "pathname": "/foo/bar/baz" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [{ "protocol": "https", "hostname": "example.com", + "pathname": "/foo/bar", "search": "otherquery", + "hash": "otherhash" }], + "exactly_empty_components": [ "port" ], + "expected_match": { + "hash": { "input": "otherhash", "groups": { "0": "otherhash" } }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "otherquery", "groups": { "0": "otherquery" } } + } + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com" }], + "inputs": [{ "protocol": "https", "hostname": "example.com", + "pathname": "/foo/bar", "search": "otherquery", + "hash": "otherhash" }], + "exactly_empty_components": [ "port" ], + "expected_match": { + "hash": { "input": "otherhash", "groups": { "0": "otherhash" } }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "otherquery", "groups": { "0": "otherquery" } } + } + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?otherquery#otherhash" }], + "inputs": [{ "protocol": "https", "hostname": "example.com", + "pathname": "/foo/bar", "search": "otherquery", + "hash": "otherhash" }], + "exactly_empty_components": [ "port" ], + "expected_match": { + "hash": { "input": "otherhash", "groups": { "0": "otherhash" } }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "otherquery", "groups": { "0": "otherquery" } } + } + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [ "https://example.com/foo/bar" ], + "exactly_empty_components": [ "port" ], + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [ "https://example.com/foo/bar?otherquery#otherhash" ], + "exactly_empty_components": [ "port" ], + "expected_match": { + "hash": { "input": "otherhash", "groups": { "0": "otherhash" } }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "otherquery", "groups": { "0": "otherquery" } } + } + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [ "https://example.com/foo/bar?query#hash" ], + "exactly_empty_components": [ "port" ], + "expected_match": { + "hash": { "input": "hash", "groups": { "0": "hash" } }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "query", "groups": { "0": "query" } } + } + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [ "https://example.com/foo/bar/baz" ], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [ "https://other.com/foo/bar" ], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [ "http://other.com/foo/bar" ], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [{ "pathname": "/foo/bar", "baseURL": "https://example.com" }], + "exactly_empty_components": [ "port" ], + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "exactly_empty_components": [ "port" ], + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [{ "pathname": "/foo/bar/baz", + "baseURL": "https://example.com" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [{ "pathname": "/foo/bar", "baseURL": "https://other.com" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar", + "baseURL": "https://example.com?query#hash" }], + "inputs": [{ "pathname": "/foo/bar", "baseURL": "http://example.com" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/([^\\/]+?)" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar" }], + "inputs": [{ "pathname": "/foo/index.html" }], + "expected_match": { + "pathname": { "input": "/foo/index.html", "groups": { "bar": "index.html" } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar" }], + "inputs": [{ "pathname": "/foo/bar/" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/(.*)" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_obj": { + "pathname": "/foo/*" + }, + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/*" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_obj": { + "pathname": "/foo/*" + }, + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/*" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_obj": { + "pathname": "/foo/*" + }, + "expected_match": { + "pathname": { "input": "/foo/", "groups": { "0": "" } } + } + }, + { + "pattern": [{ "pathname": "/foo/*" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": { + "pathname": { "input": "/foo/", "groups": { "0": "" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)" }], + "inputs": [{ "pathname": "/foo" }], + "expected_obj": { + "pathname": "/foo/*" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/*" }], + "inputs": [{ "pathname": "/foo" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar(.*)" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar(.*)" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "bar": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar(.*)" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": { + "pathname": { "input": "/foo/", "groups": { "bar": "" } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar(.*)" }], + "inputs": [{ "pathname": "/foo" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar?" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar?" }], + "inputs": [{ "pathname": "/foo" }], + "//": "The `null` below is translated to undefined in the test harness.", + "expected_match": { + "pathname": { "input": "/foo", "groups": { "bar": null } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar?" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar?" }], + "inputs": [{ "pathname": "/foobar" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar?" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar+" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar+" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "bar": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar+" }], + "inputs": [{ "pathname": "/foo" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar+" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar+" }], + "inputs": [{ "pathname": "/foobar" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar*" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar*" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "bar": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar*" }], + "inputs": [{ "pathname": "/foo" }], + "//": "The `null` below is translated to undefined in the test harness.", + "expected_match": { + "pathname": { "input": "/foo", "groups": { "bar": null } } + } + }, + { + "pattern": [{ "pathname": "/foo/:bar*" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/:bar*" }], + "inputs": [{ "pathname": "/foobar" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/(.*)?" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_obj": { + "pathname": "/foo/*?" + }, + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/*?" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)?" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_obj": { + "pathname": "/foo/*?" + }, + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/*?" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)?" }], + "inputs": [{ "pathname": "/foo" }], + "expected_obj": { + "pathname": "/foo/*?" + }, + "//": "The `null` below is translated to undefined in the test harness.", + "expected_match": { + "pathname": { "input": "/foo", "groups": { "0": null } } + } + }, + { + "pattern": [{ "pathname": "/foo/*?" }], + "inputs": [{ "pathname": "/foo" }], + "//": "The `null` below is translated to undefined in the test harness.", + "expected_match": { + "pathname": { "input": "/foo", "groups": { "0": null } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)?" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_obj": { + "pathname": "/foo/*?" + }, + "expected_match": { + "pathname": { "input": "/foo/", "groups": { "0": "" } } + } + }, + { + "pattern": [{ "pathname": "/foo/*?" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": { + "pathname": { "input": "/foo/", "groups": { "0": "" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)?" }], + "inputs": [{ "pathname": "/foobar" }], + "expected_obj": { + "pathname": "/foo/*?" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/*?" }], + "inputs": [{ "pathname": "/foobar" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/(.*)?" }], + "inputs": [{ "pathname": "/fo" }], + "expected_obj": { + "pathname": "/foo/*?" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/*?" }], + "inputs": [{ "pathname": "/fo" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/(.*)+" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_obj": { + "pathname": "/foo/*+" + }, + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/*+" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)+" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_obj": { + "pathname": "/foo/*+" + }, + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/*+" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)+" }], + "inputs": [{ "pathname": "/foo" }], + "expected_obj": { + "pathname": "/foo/*+" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/*+" }], + "inputs": [{ "pathname": "/foo" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/(.*)+" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_obj": { + "pathname": "/foo/*+" + }, + "expected_match": { + "pathname": { "input": "/foo/", "groups": { "0": "" } } + } + }, + { + "pattern": [{ "pathname": "/foo/*+" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": { + "pathname": { "input": "/foo/", "groups": { "0": "" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)+" }], + "inputs": [{ "pathname": "/foobar" }], + "expected_obj": { + "pathname": "/foo/*+" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/*+" }], + "inputs": [{ "pathname": "/foobar" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/(.*)+" }], + "inputs": [{ "pathname": "/fo" }], + "expected_obj": { + "pathname": "/foo/*+" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/*+" }], + "inputs": [{ "pathname": "/fo" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/(.*)*" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_obj": { + "pathname": "/foo/**" + }, + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/**" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)*" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_obj": { + "pathname": "/foo/**" + }, + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/**" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": { + "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)*" }], + "inputs": [{ "pathname": "/foo" }], + "expected_obj": { + "pathname": "/foo/**" + }, + "//": "The `null` below is translated to undefined in the test harness.", + "expected_match": { + "pathname": { "input": "/foo", "groups": { "0": null } } + } + }, + { + "pattern": [{ "pathname": "/foo/**" }], + "inputs": [{ "pathname": "/foo" }], + "//": "The `null` below is translated to undefined in the test harness.", + "expected_match": { + "pathname": { "input": "/foo", "groups": { "0": null } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)*" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_obj": { + "pathname": "/foo/**" + }, + "expected_match": { + "pathname": { "input": "/foo/", "groups": { "0": "" } } + } + }, + { + "pattern": [{ "pathname": "/foo/**" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": { + "pathname": { "input": "/foo/", "groups": { "0": "" } } + } + }, + { + "pattern": [{ "pathname": "/foo/(.*)*" }], + "inputs": [{ "pathname": "/foobar" }], + "expected_obj": { + "pathname": "/foo/**" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/**" }], + "inputs": [{ "pathname": "/foobar" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/(.*)*" }], + "inputs": [{ "pathname": "/fo" }], + "expected_obj": { + "pathname": "/foo/**" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/**" }], + "inputs": [{ "pathname": "/fo" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo{/bar}" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_obj": { + "pathname": "/foo/bar" + }, + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo{/bar}" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_obj": { + "pathname": "/foo/bar" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo{/bar}" }], + "inputs": [{ "pathname": "/foo" }], + "expected_obj": { + "pathname": "/foo/bar" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo{/bar}" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_obj": { + "pathname": "/foo/bar" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo{/bar}?" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo{/bar}?" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo{/bar}?" }], + "inputs": [{ "pathname": "/foo" }], + "expected_match": { + "pathname": { "input": "/foo", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo{/bar}?" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo{/bar}+" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo{/bar}+" }], + "inputs": [{ "pathname": "/foo/bar/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar/bar", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo{/bar}+" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo{/bar}+" }], + "inputs": [{ "pathname": "/foo" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo{/bar}+" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo{/bar}*" }], + "inputs": [{ "pathname": "/foo/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo{/bar}*" }], + "inputs": [{ "pathname": "/foo/bar/bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar/bar", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo{/bar}*" }], + "inputs": [{ "pathname": "/foo/bar/baz" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo{/bar}*" }], + "inputs": [{ "pathname": "/foo" }], + "expected_match": { + "pathname": { "input": "/foo", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/foo{/bar}*" }], + "inputs": [{ "pathname": "/foo/" }], + "expected_match": null + }, + { + "pattern": [{ "protocol": "(café)" }], + "expected_obj": "error" + }, + { + "pattern": [{ "username": "(café)" }], + "expected_obj": "error" + }, + { + "pattern": [{ "password": "(café)" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "(café)" }], + "expected_obj": "error" + }, + { + "pattern": [{ "pathname": "(café)" }], + "expected_obj": "error" + }, + { + "pattern": [{ "search": "(café)" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hash": "(café)" }], + "expected_obj": "error" + }, + { + "pattern": [{ "protocol": ":café" }], + "inputs": [{ "protocol": "foo" }], + "expected_match": { + "protocol": { "input": "foo", "groups": { "café": "foo" } } + } + }, + { + "pattern": [{ "username": ":café" }], + "inputs": [{ "username": "foo" }], + "expected_match": { + "username": { "input": "foo", "groups": { "café": "foo" } } + } + }, + { + "pattern": [{ "password": ":café" }], + "inputs": [{ "password": "foo" }], + "expected_match": { + "password": { "input": "foo", "groups": { "café": "foo" } } + } + }, + { + "pattern": [{ "hostname": ":café" }], + "inputs": [{ "hostname": "foo" }], + "expected_match": { + "hostname": { "input": "foo", "groups": { "café": "foo" } } + } + }, + { + "pattern": [{ "pathname": "/:café" }], + "inputs": [{ "pathname": "/foo" }], + "expected_match": { + "pathname": { "input": "/foo", "groups": { "café": "foo" } } + } + }, + { + "pattern": [{ "search": ":café" }], + "inputs": [{ "search": "foo" }], + "expected_match": { + "search": { "input": "foo", "groups": { "café": "foo" } } + } + }, + { + "pattern": [{ "hash": ":café" }], + "inputs": [{ "hash": "foo" }], + "expected_match": { + "hash": { "input": "foo", "groups": { "café": "foo" } } + } + }, + { + "pattern": [{ "protocol": ":\u2118" }], + "inputs": [{ "protocol": "foo" }], + "expected_match": { + "protocol": { "input": "foo", "groups": { "\u2118": "foo" } } + } + }, + { + "pattern": [{ "username": ":\u2118" }], + "inputs": [{ "username": "foo" }], + "expected_match": { + "username": { "input": "foo", "groups": { "\u2118": "foo" } } + } + }, + { + "pattern": [{ "password": ":\u2118" }], + "inputs": [{ "password": "foo" }], + "expected_match": { + "password": { "input": "foo", "groups": { "\u2118": "foo" } } + } + }, + { + "pattern": [{ "hostname": ":\u2118" }], + "inputs": [{ "hostname": "foo" }], + "expected_match": { + "hostname": { "input": "foo", "groups": { "\u2118": "foo" } } + } + }, + { + "pattern": [{ "pathname": "/:\u2118" }], + "inputs": [{ "pathname": "/foo" }], + "expected_match": { + "pathname": { "input": "/foo", "groups": { "\u2118": "foo" } } + } + }, + { + "pattern": [{ "search": ":\u2118" }], + "inputs": [{ "search": "foo" }], + "expected_match": { + "search": { "input": "foo", "groups": { "\u2118": "foo" } } + } + }, + { + "pattern": [{ "hash": ":\u2118" }], + "inputs": [{ "hash": "foo" }], + "expected_match": { + "hash": { "input": "foo", "groups": { "\u2118": "foo" } } + } + }, + { + "pattern": [{ "protocol": ":\u3400" }], + "inputs": [{ "protocol": "foo" }], + "expected_match": { + "protocol": { "input": "foo", "groups": { "\u3400": "foo" } } + } + }, + { + "pattern": [{ "username": ":\u3400" }], + "inputs": [{ "username": "foo" }], + "expected_match": { + "username": { "input": "foo", "groups": { "\u3400": "foo" } } + } + }, + { + "pattern": [{ "password": ":\u3400" }], + "inputs": [{ "password": "foo" }], + "expected_match": { + "password": { "input": "foo", "groups": { "\u3400": "foo" } } + } + }, + { + "pattern": [{ "hostname": ":\u3400" }], + "inputs": [{ "hostname": "foo" }], + "expected_match": { + "hostname": { "input": "foo", "groups": { "\u3400": "foo" } } + } + }, + { + "pattern": [{ "pathname": "/:\u3400" }], + "inputs": [{ "pathname": "/foo" }], + "expected_match": { + "pathname": { "input": "/foo", "groups": { "\u3400": "foo" } } + } + }, + { + "pattern": [{ "search": ":\u3400" }], + "inputs": [{ "search": "foo" }], + "expected_match": { + "search": { "input": "foo", "groups": { "\u3400": "foo" } } + } + }, + { + "pattern": [{ "hash": ":\u3400" }], + "inputs": [{ "hash": "foo" }], + "expected_match": { + "hash": { "input": "foo", "groups": { "\u3400": "foo" } } + } + }, + { + "pattern": [{ "protocol": "(.*)" }], + "inputs": [{ "protocol" : "café" }], + "expected_obj": { + "protocol": "*" + }, + "expected_match": null + }, + { + "pattern": [{ "protocol": "(.*)" }], + "inputs": [{ "protocol": "cafe" }], + "expected_obj": { + "protocol": "*" + }, + "expected_match": { + "protocol": { "input": "cafe", "groups": { "0": "cafe" }} + } + }, + { + "pattern": [{ "protocol": "foo-bar" }], + "inputs": [{ "protocol": "foo-bar" }], + "expected_match": { + "protocol": { "input": "foo-bar", "groups": {} } + } + }, + { + "pattern": [{ "username": "caf%C3%A9" }], + "inputs": [{ "username" : "café" }], + "expected_match": { + "username": { "input": "caf%C3%A9", "groups": {}} + } + }, + { + "pattern": [{ "username": "café" }], + "inputs": [{ "username" : "café" }], + "expected_obj": { + "username": "caf%C3%A9" + }, + "expected_match": { + "username": { "input": "caf%C3%A9", "groups": {}} + } + }, + { + "pattern": [{ "username": "caf%c3%a9" }], + "inputs": [{ "username" : "café" }], + "expected_match": null + }, + { + "pattern": [{ "password": "caf%C3%A9" }], + "inputs": [{ "password" : "café" }], + "expected_match": { + "password": { "input": "caf%C3%A9", "groups": {}} + } + }, + { + "pattern": [{ "password": "café" }], + "inputs": [{ "password" : "café" }], + "expected_obj": { + "password": "caf%C3%A9" + }, + "expected_match": { + "password": { "input": "caf%C3%A9", "groups": {}} + } + }, + { + "pattern": [{ "password": "caf%c3%a9" }], + "inputs": [{ "password" : "café" }], + "expected_match": null + }, + { + "pattern": [{ "hostname": "xn--caf-dma.com" }], + "inputs": [{ "hostname" : "café.com" }], + "expected_match": { + "hostname": { "input": "xn--caf-dma.com", "groups": {}} + } + }, + { + "pattern": [{ "hostname": "café.com" }], + "inputs": [{ "hostname" : "café.com" }], + "expected_obj": { + "hostname": "xn--caf-dma.com" + }, + "expected_match": { + "hostname": { "input": "xn--caf-dma.com", "groups": {}} + } + }, + { + "pattern": [{ "port": "" }], + "inputs": [{ "protocol": "http", "port": "80" }], + "exactly_empty_components": [ "port" ], + "expected_match": { + "protocol": { "input": "http", "groups": { "0": "http" }} + } + }, + { + "pattern": [{ "protocol": "http", "port": "80" }], + "inputs": [{ "protocol": "http", "port": "80" }], + "exactly_empty_components": [ "port" ], + "expected_match": { + "protocol": { "input": "http", "groups": {}} + } + }, + { + "pattern": [{ "protocol": "http", "port": "80{20}?" }], + "inputs": [{ "protocol": "http", "port": "80" }], + "expected_match": null + }, + { + "pattern": [{ "protocol": "http", "port": "80 " }], + "inputs": [{ "protocol": "http", "port": "80" }], + "expected_obj": "error" + }, + { + "pattern": [{ "port": "80" }], + "inputs": [{ "protocol": "http", "port": "80" }], + "expected_match": null + }, + { + "pattern": [{ "protocol": "http{s}?", "port": "80" }], + "inputs": [{ "protocol": "http", "port": "80" }], + "expected_match": null + }, + { + "pattern": [{ "port": "80" }], + "inputs": [{ "port": "80" }], + "expected_match": { + "port": { "input": "80", "groups": {}} + } + }, + { + "pattern": [{ "port": "(.*)" }], + "inputs": [{ "port": "invalid80" }], + "expected_obj": { + "port": "*" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "pathname": "/foo/./bar" }], + "expected_match": { + "pathname": { "input": "/foo/bar", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "/foo/baz" }], + "inputs": [{ "pathname": "/foo/bar/../baz" }], + "expected_match": { + "pathname": { "input": "/foo/baz", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "/caf%C3%A9" }], + "inputs": [{ "pathname": "/café" }], + "expected_match": { + "pathname": { "input": "/caf%C3%A9", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "/café" }], + "inputs": [{ "pathname": "/café" }], + "expected_obj": { + "pathname": "/caf%C3%A9" + }, + "expected_match": { + "pathname": { "input": "/caf%C3%A9", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "/caf%c3%a9" }], + "inputs": [{ "pathname": "/café" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "pathname": "foo/bar" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [{ "pathname": "foo/bar", "baseURL": "https://example.com" }], + "expected_match": { + "protocol": { "input": "https", "groups": { "0": "https" }}, + "hostname": { "input": "example.com", "groups": { "0": "example.com" }}, + "pathname": { "input": "/foo/bar", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "/foo/../bar" }], + "inputs": [{ "pathname": "/bar" }], + "expected_obj": { + "pathname": "/bar" + }, + "expected_match": { + "pathname": { "input": "/bar", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "./foo/bar", "baseURL": "https://example.com" }], + "inputs": [{ "pathname": "foo/bar", "baseURL": "https://example.com" }], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "pathname": "/foo/bar" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {}}, + "hostname": { "input": "example.com", "groups": {}}, + "pathname": { "input": "/foo/bar", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "", "baseURL": "https://example.com" }], + "inputs": [{ "pathname": "/", "baseURL": "https://example.com" }], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "pathname": "/" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {}}, + "hostname": { "input": "example.com", "groups": {}}, + "pathname": { "input": "/", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "{/bar}", "baseURL": "https://example.com/foo/" }], + "inputs": [{ "pathname": "./bar", "baseURL": "https://example.com/foo/" }], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "pathname": "/bar" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "\\/bar", "baseURL": "https://example.com/foo/" }], + "inputs": [{ "pathname": "./bar", "baseURL": "https://example.com/foo/" }], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "pathname": "/bar" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "b", "baseURL": "https://example.com/foo/" }], + "inputs": [{ "pathname": "./b", "baseURL": "https://example.com/foo/" }], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "pathname": "/foo/b" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {}}, + "hostname": { "input": "example.com", "groups": {}}, + "pathname": { "input": "/foo/b", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "foo/bar" }], + "inputs": [ "https://example.com/foo/bar" ], + "expected_match": null + }, + { + "pattern": [{ "pathname": "foo/bar", "baseURL": "https://example.com" }], + "inputs": [ "https://example.com/foo/bar" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "pathname": "/foo/bar" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {}}, + "hostname": { "input": "example.com", "groups": {}}, + "pathname": { "input": "/foo/bar", "groups": {}} + } + }, + { + "pattern": [{ "pathname": ":name.html", "baseURL": "https://example.com" }], + "inputs": [ "https://example.com/foo.html"] , + "exactly_empty_components": [ "port" ], + "expected_obj": { + "pathname": "/:name.html" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {}}, + "hostname": { "input": "example.com", "groups": {}}, + "pathname": { "input": "/foo.html", "groups": { "name": "foo" }} + } + }, + { + "pattern": [{ "search": "q=caf%C3%A9" }], + "inputs": [{ "search": "q=café" }], + "expected_match": { + "search": { "input": "q=caf%C3%A9", "groups": {}} + } + }, + { + "pattern": [{ "search": "q=café" }], + "inputs": [{ "search": "q=café" }], + "expected_obj": { + "search": "q=caf%C3%A9" + }, + "expected_match": { + "search": { "input": "q=caf%C3%A9", "groups": {}} + } + }, + { + "pattern": [{ "search": "q=caf%c3%a9" }], + "inputs": [{ "search": "q=café" }], + "expected_match": null + }, + { + "pattern": [{ "hash": "caf%C3%A9" }], + "inputs": [{ "hash": "café" }], + "expected_match": { + "hash": { "input": "caf%C3%A9", "groups": {}} + } + }, + { + "pattern": [{ "hash": "café" }], + "inputs": [{ "hash": "café" }], + "expected_obj": { + "hash": "caf%C3%A9" + }, + "expected_match": { + "hash": { "input": "caf%C3%A9", "groups": {}} + } + }, + { + "pattern": [{ "hash": "caf%c3%a9" }], + "inputs": [{ "hash": "café" }], + "expected_match": null + }, + { + "pattern": [{ "protocol": "about", "pathname": "(blank|sourcedoc)" }], + "inputs": [ "about:blank" ], + "expected_match": { + "protocol": { "input": "about", "groups": {}}, + "pathname": { "input": "blank", "groups": { "0": "blank" }} + } + }, + { + "pattern": [{ "protocol": "data", "pathname": ":number([0-9]+)" }], + "inputs": [ "data:8675309" ], + "expected_match": { + "protocol": { "input": "data", "groups": {}}, + "pathname": { "input": "8675309", "groups": { "number": "8675309" }} + } + }, + { + "pattern": [{ "pathname": "/(\\m)" }], + "expected_obj": "error" + }, + { + "pattern": [{ "pathname": "/foo!" }], + "inputs": [{ "pathname": "/foo!" }], + "expected_match": { + "pathname": { "input": "/foo!", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "/foo\\:" }], + "inputs": [{ "pathname": "/foo:" }], + "expected_match": { + "pathname": { "input": "/foo:", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "/foo\\{" }], + "inputs": [{ "pathname": "/foo{" }], + "expected_obj": { + "pathname": "/foo%7B" + }, + "expected_match": { + "pathname": { "input": "/foo%7B", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "/foo\\(" }], + "inputs": [{ "pathname": "/foo(" }], + "expected_match": { + "pathname": { "input": "/foo(", "groups": {}} + } + }, + { + "pattern": [{ "protocol": "javascript", "pathname": "var x = 1;" }], + "inputs": [{ "protocol": "javascript", "pathname": "var x = 1;" }], + "expected_match": { + "protocol": { "input": "javascript", "groups": {}}, + "pathname": { "input": "var x = 1;", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "var x = 1;" }], + "inputs": [{ "protocol": "javascript", "pathname": "var x = 1;" }], + "expected_obj": { + "pathname": "var%20x%20=%201;" + }, + "expected_match": null + }, + { + "pattern": [{ "protocol": "javascript", "pathname": "var x = 1;" }], + "inputs": [{ "baseURL": "javascript:var x = 1;" }], + "expected_match": { + "protocol": { "input": "javascript", "groups": {}}, + "pathname": { "input": "var x = 1;", "groups": {}} + } + }, + { + "pattern": [{ "protocol": "(data|javascript)", "pathname": "var x = 1;" }], + "inputs": [{ "protocol": "javascript", "pathname": "var x = 1;" }], + "expected_match": { + "protocol": { "input": "javascript", "groups": {"0": "javascript"}}, + "pathname": { "input": "var x = 1;", "groups": {}} + } + }, + { + "pattern": [{ "protocol": "(https|javascript)", "pathname": "var x = 1;" }], + "inputs": [{ "protocol": "javascript", "pathname": "var x = 1;" }], + "expected_obj": { + "pathname": "var%20x%20=%201;" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "var x = 1;" }], + "inputs": [{ "pathname": "var x = 1;" }], + "expected_obj": { + "pathname": "var%20x%20=%201;" + }, + "expected_match": { + "pathname": { "input": "var%20x%20=%201;", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [ "./foo/bar", "https://example.com" ], + "expected_match": { + "hostname": { "input": "example.com", "groups": { "0": "example.com" } }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": { "0": "https" } } + } + }, + { + "pattern": [{ "pathname": "/foo/bar" }], + "inputs": [ { "pathname": "/foo/bar" }, "https://example.com" ], + "expected_match": "error" + }, + { + "pattern": [ "https://example.com:8080/foo?bar#baz" ], + "inputs": [{ "pathname": "/foo", "search": "bar", "hash": "baz", + "baseURL": "https://example.com:8080" }], + "expected_obj": { + "protocol": "https", + "username": "*", + "password": "*", + "hostname": "example.com", + "port": "8080", + "pathname": "/foo", + "search": "bar", + "hash": "baz" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "port": { "input": "8080", "groups": {} }, + "pathname": { "input": "/foo", "groups": {} }, + "search": { "input": "bar", "groups": {} }, + "hash": { "input": "baz", "groups": {} } + } + }, + { + "pattern": [ "/foo?bar#baz", "https://example.com:8080" ], + "inputs": [{ "pathname": "/foo", "search": "bar", "hash": "baz", + "baseURL": "https://example.com:8080" }], + "expected_obj": { + "pathname": "/foo", + "search": "bar", + "hash": "baz" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "port": { "input": "8080", "groups": {} }, + "pathname": { "input": "/foo", "groups": {} }, + "search": { "input": "bar", "groups": {} }, + "hash": { "input": "baz", "groups": {} } + } + }, + { + "pattern": [ "/foo" ], + "expected_obj": "error" + }, + { + "pattern": [ "example.com/foo" ], + "expected_obj": "error" + }, + { + "pattern": [ "http{s}?://{*.}?example.com/:product/:endpoint" ], + "inputs": [ "https://sub.example.com/foo/bar" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "http{s}?", + "hostname": "{*.}?example.com", + "pathname": "/:product/:endpoint" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "sub.example.com", "groups": { "0": "sub" } }, + "pathname": { "input": "/foo/bar", "groups": { "product": "foo", + "endpoint": "bar" } } + } + }, + { + "pattern": [ "https://example.com?foo" ], + "inputs": [ "https://example.com/?foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/", + "search": "foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": {} }, + "search": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "https://example.com#foo" ], + "inputs": [ "https://example.com/#foo" ], + "exactly_empty_components": [ "port", "search" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/", + "hash": "foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": {} }, + "hash": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "https://example.com:8080?foo" ], + "inputs": [ "https://example.com:8080/?foo" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "port": "8080", + "pathname": "/", + "search": "foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "port": { "input": "8080", "groups": {} }, + "pathname": { "input": "/", "groups": {} }, + "search": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "https://example.com:8080#foo" ], + "inputs": [ "https://example.com:8080/#foo" ], + "exactly_empty_components": [ "search" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "port": "8080", + "pathname": "/", + "hash": "foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "port": { "input": "8080", "groups": {} }, + "pathname": { "input": "/", "groups": {} }, + "hash": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "https://example.com/?foo" ], + "inputs": [ "https://example.com/?foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/", + "search": "foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": {} }, + "search": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "https://example.com/#foo" ], + "inputs": [ "https://example.com/#foo" ], + "exactly_empty_components": [ "port", "search" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/", + "hash": "foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": {} }, + "hash": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "https://example.com/*?foo" ], + "inputs": [ "https://example.com/?foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/*?foo" + }, + "expected_match": null + }, + { + "pattern": [ "https://example.com/*\\?foo" ], + "inputs": [ "https://example.com/?foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/*", + "search": "foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": { "0": "" } }, + "search": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "https://example.com/:name?foo" ], + "inputs": [ "https://example.com/bar?foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/:name?foo" + }, + "expected_match": null + }, + { + "pattern": [ "https://example.com/:name\\?foo" ], + "inputs": [ "https://example.com/bar?foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/:name", + "search": "foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/bar", "groups": { "name": "bar" } }, + "search": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "https://example.com/(bar)?foo" ], + "inputs": [ "https://example.com/bar?foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/(bar)?foo" + }, + "expected_match": null + }, + { + "pattern": [ "https://example.com/(bar)\\?foo" ], + "inputs": [ "https://example.com/bar?foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/(bar)", + "search": "foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/bar", "groups": { "0": "bar" } }, + "search": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "https://example.com/{bar}?foo" ], + "inputs": [ "https://example.com/bar?foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/{bar}?foo" + }, + "expected_match": null + }, + { + "pattern": [ "https://example.com/{bar}\\?foo" ], + "inputs": [ "https://example.com/bar?foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/bar", + "search": "foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/bar", "groups": {} }, + "search": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "https://example.com/" ], + "inputs": [ "https://example.com:8080/" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "port": "", + "pathname": "/" + }, + "expected_match": null + }, + { + "pattern": [ "data:foobar" ], + "inputs": [ "data:foobar" ], + "expected_obj": "error" + }, + { + "pattern": [ "data\\:foobar" ], + "inputs": [ "data:foobar" ], + "exactly_empty_components": [ "hostname", "port" ], + "expected_obj": { + "protocol": "data", + "pathname": "foobar" + }, + "expected_match": { + "protocol": { "input": "data", "groups": {} }, + "pathname": { "input": "foobar", "groups": {} } + } + }, + { + "pattern": [ "https://{sub.}?example.com/foo" ], + "inputs": [ "https://example.com/foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "{sub.}?example.com", + "pathname": "/foo" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo", "groups": {} } + } + }, + { + "pattern": [ "https://{sub.}?example{.com/}foo" ], + "inputs": [ "https://example.com/foo" ], + "expected_obj": "error" + }, + { + "pattern": [ "{https://}example.com/foo" ], + "inputs": [ "https://example.com/foo" ], + "expected_obj": "error" + }, + { + "pattern": [ "https://(sub.)?example.com/foo" ], + "inputs": [ "https://example.com/foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "(sub.)?example.com", + "pathname": "/foo" + }, + "//": "The `null` below is translated to undefined in the test harness.", + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": { "0": null } }, + "pathname": { "input": "/foo", "groups": {} } + } + }, + { + "pattern": [ "https://(sub.)?example(.com/)foo" ], + "inputs": [ "https://example.com/foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "(sub.)?example(.com/)foo", + "pathname": "*" + }, + "expected_match": null + }, + { + "pattern": [ "(https://)example.com/foo" ], + "inputs": [ "https://example.com/foo" ], + "expected_obj": "error" + }, + { + "pattern": [ "https://{sub{.}}example.com/foo" ], + "inputs": [ "https://example.com/foo" ], + "expected_obj": "error" + }, + { + "pattern": [ "https://(sub(?:.))?example.com/foo" ], + "inputs": [ "https://example.com/foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "(sub(?:.))?example.com", + "pathname": "/foo" + }, + "//": "The `null` below is translated to undefined in the test harness.", + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": { "0": null } }, + "pathname": { "input": "/foo", "groups": {} } + } + }, + { + "pattern": [ "file:///foo/bar" ], + "inputs": [ "file:///foo/bar" ], + "exactly_empty_components": [ "hostname", "port" ], + "expected_obj": { + "protocol": "file", + "pathname": "/foo/bar" + }, + "expected_match": { + "protocol": { "input": "file", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} } + } + }, + { + "pattern": [ "data:" ], + "inputs": [ "data:" ], + "exactly_empty_components": [ "hostname", "port", "pathname" ], + "expected_obj": { + "protocol": "data" + }, + "expected_match": { + "protocol": { "input": "data", "groups": {} } + } + }, + { + "pattern": [ "foo://bar" ], + "inputs": [ "foo://bad_url_browser_interop" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "foo", + "hostname": "bar" + }, + "expected_match": null + }, + { + "pattern": [ "(café)://foo" ], + "expected_obj": "error" + }, + { + "pattern": [ "https://example.com/foo?bar#baz" ], + "inputs": [{ "protocol": "https:", + "search": "?bar", + "hash": "#baz", + "baseURL": "http://example.com/foo" }], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/foo", + "search": "bar", + "hash": "baz" + }, + "expected_match": null + }, + { + "pattern": [{ "protocol": "http{s}?:", + "search": "?bar", + "hash": "#baz" }], + "inputs": [ "http://example.com/foo?bar#baz" ], + "expected_obj": { + "protocol": "http{s}?", + "search": "bar", + "hash": "baz" + }, + "expected_match": { + "protocol": { "input": "http", "groups": {} }, + "hostname": { "input": "example.com", "groups": { "0": "example.com" }}, + "pathname": { "input": "/foo", "groups": { "0": "/foo" }}, + "search": { "input": "bar", "groups": {} }, + "hash": { "input": "baz", "groups": {} } + } + }, + { + "pattern": [ "?bar#baz", "https://example.com/foo" ], + "inputs": [ "?bar#baz", "https://example.com/foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/foo", + "search": "bar", + "hash": "baz" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo", "groups": {} }, + "search": { "input": "bar", "groups": {} }, + "hash": { "input": "baz", "groups": {} } + } + }, + { + "pattern": [ "?bar", "https://example.com/foo#baz" ], + "inputs": [ "?bar", "https://example.com/foo#snafu" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/foo", + "search": "bar", + "hash": "*" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo", "groups": {} }, + "search": { "input": "bar", "groups": {} } + } + }, + { + "pattern": [ "#baz", "https://example.com/foo?bar" ], + "inputs": [ "#baz", "https://example.com/foo?bar" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/foo", + "search": "bar", + "hash": "baz" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo", "groups": {} }, + "search": { "input": "bar", "groups": {} }, + "hash": { "input": "baz", "groups": {} } + } + }, + { + "pattern": [ "#baz", "https://example.com/foo" ], + "inputs": [ "#baz", "https://example.com/foo" ], + "exactly_empty_components": [ "port", "search" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/foo", + "hash": "baz" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo", "groups": {} }, + "hash": { "input": "baz", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "*" }], + "inputs": [ "foo", "data:data-urls-cannot-be-base-urls" ], + "expected_match": null + }, + { + "pattern": [{ "pathname": "*" }], + "inputs": [ "foo", "not|a|valid|url" ], + "expected_match": null + }, + { + "pattern": [ "https://foo\\:bar@example.com" ], + "inputs": [ "https://foo:bar@example.com" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "username": "foo", + "password": "bar", + "hostname": "example.com", + "pathname": "*" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "username": { "input": "foo", "groups": {} }, + "password": { "input": "bar", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": { "0": "/" } } + } + }, + { + "pattern": [ "https://foo@example.com" ], + "inputs": [ "https://foo@example.com" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "username": "foo", + "hostname": "example.com", + "pathname": "*" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "username": { "input": "foo", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": { "0": "/" } } + } + }, + { + "pattern": [ "https://\\:bar@example.com" ], + "inputs": [ "https://:bar@example.com" ], + "exactly_empty_components": [ "username", "port" ], + "expected_obj": { + "protocol": "https", + "password": "bar", + "hostname": "example.com", + "pathname": "*" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "password": { "input": "bar", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": { "0": "/" } } + } + }, + { + "pattern": [ "https://:user::pass@example.com" ], + "inputs": [ "https://foo:bar@example.com" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "username": ":user", + "password": ":pass", + "hostname": "example.com", + "pathname": "*" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "username": { "input": "foo", "groups": { "user": "foo" } }, + "password": { "input": "bar", "groups": { "pass": "bar" } }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": { "0": "/" } } + } + }, + { + "pattern": [ "https\\:foo\\:bar@example.com" ], + "inputs": [ "https:foo:bar@example.com" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "username": "foo", + "password": "bar", + "hostname": "example.com", + "pathname": "*" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "username": { "input": "foo", "groups": {} }, + "password": { "input": "bar", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": { "0": "/" } } + } + }, + { + "pattern": [ "data\\:foo\\:bar@example.com" ], + "inputs": [ "data:foo:bar@example.com" ], + "exactly_empty_components": [ "hostname", "port" ], + "expected_obj": { + "protocol": "data", + "pathname": "foo\\:bar@example.com" + }, + "expected_match": { + "protocol": { "input": "data", "groups": {} }, + "pathname": { "input": "foo:bar@example.com", "groups": {} } + } + }, + { + "pattern": [ "https://foo{\\:}bar@example.com" ], + "inputs": [ "https://foo:bar@example.com" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "username": "foo%3Abar", + "hostname": "example.com" + }, + "expected_match": null + }, + { + "pattern": [ "data{\\:}channel.html", "https://example.com" ], + "inputs": [ "https://example.com/data:channel.html" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "pathname": "/data\\:channel.html", + "search": "*", + "hash": "*" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/data:channel.html", "groups": {} } + } + }, + { + "pattern": [ "http://[\\:\\:1]/" ], + "inputs": [ "http://[::1]/" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "http", + "hostname": "[\\:\\:1]", + "pathname": "/" + }, + "expected_match": { + "protocol": { "input": "http", "groups": {} }, + "hostname": { "input": "[::1]", "groups": {} }, + "pathname": { "input": "/", "groups": {} } + } + }, + { + "pattern": [ "http://[\\:\\:1]:8080/" ], + "inputs": [ "http://[::1]:8080/" ], + "expected_obj": { + "protocol": "http", + "hostname": "[\\:\\:1]", + "port": "8080", + "pathname": "/" + }, + "expected_match": { + "protocol": { "input": "http", "groups": {} }, + "hostname": { "input": "[::1]", "groups": {} }, + "port": { "input": "8080", "groups": {} }, + "pathname": { "input": "/", "groups": {} } + } + }, + { + "pattern": [ "http://[\\:\\:a]/" ], + "inputs": [ "http://[::a]/" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "http", + "hostname": "[\\:\\:a]", + "pathname": "/" + }, + "expected_match": { + "protocol": { "input": "http", "groups": {} }, + "hostname": { "input": "[::a]", "groups": {} }, + "pathname": { "input": "/", "groups": {} } + } + }, + { + "pattern": [ "http://[:address]/" ], + "inputs": [ "http://[::1]/" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "http", + "hostname": "[:address]", + "pathname": "/" + }, + "expected_match": { + "protocol": { "input": "http", "groups": {} }, + "hostname": { "input": "[::1]", "groups": { "address": "::1" }}, + "pathname": { "input": "/", "groups": {} } + } + }, + { + "pattern": [ "http://[\\:\\:AB\\::num]/" ], + "inputs": [ "http://[::ab:1]/" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "protocol": "http", + "hostname": "[\\:\\:ab\\::num]", + "pathname": "/" + }, + "expected_match": { + "protocol": { "input": "http", "groups": {} }, + "hostname": { "input": "[::ab:1]", "groups": { "num": "1" }}, + "pathname": { "input": "/", "groups": {} } + } + }, + { + "pattern": [{ "hostname": "[\\:\\:AB\\::num]" }], + "inputs": [{ "hostname": "[::ab:1]" }], + "expected_obj": { + "hostname": "[\\:\\:ab\\::num]" + }, + "expected_match": { + "hostname": { "input": "[::ab:1]", "groups": { "num": "1" }} + } + }, + { + "pattern": [{ "hostname": "[\\:\\:xY\\::num]" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "{[\\:\\:ab\\::num]}" }], + "inputs": [{ "hostname": "[::ab:1]" }], + "expected_match": { + "hostname": { "input": "[::ab:1]", "groups": { "num": "1" }} + } + }, + { + "pattern": [{ "hostname": "{[\\:\\:fé\\::num]}" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "{[\\:\\::num\\:1]}" }], + "inputs": [{ "hostname": "[::ab:1]" }], + "expected_match": { + "hostname": { "input": "[::ab:1]", "groups": { "num": "ab" }} + } + }, + { + "pattern": [{ "hostname": "{[\\:\\::num\\:fé]}" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "[*\\:1]" }], + "inputs": [{ "hostname": "[::ab:1]" }], + "expected_match": { + "hostname": { "input": "[::ab:1]", "groups": { "0": "::ab" }} + } + }, + { + "pattern": [{ "hostname": "*\\:1]" }], + "expected_obj": "error" + }, + { + "pattern": [ "https://foo{{@}}example.com" ], + "inputs": [ "https://foo@example.com" ], + "expected_obj": "error" + }, + { + "pattern": [ "https://foo{@example.com" ], + "inputs": [ "https://foo@example.com" ], + "expected_obj": "error" + }, + { + "pattern": [ "data\\:text/javascript,let x = 100/:tens?5;" ], + "inputs": [ "data:text/javascript,let x = 100/5;" ], + "exactly_empty_components": [ "hostname", "port" ], + "expected_obj": { + "protocol": "data", + "pathname": "text/javascript,let x = 100/:tens?5;" + }, + "//": "The `null` below is translated to undefined in the test harness.", + "expected_match": { + "protocol": { "input": "data", "groups": {} }, + "pathname": { "input": "text/javascript,let x = 100/5;", "groups": { "tens": null } } + } + }, + { + "pattern": [{ "pathname": "/:id/:id" }], + "expected_obj": "error" + }, + { + "pattern": [{ "pathname": "/foo", "baseURL": "" }], + "expected_obj": "error" + }, + { + "pattern": [ "/foo", "" ], + "expected_obj": "error" + }, + { + "pattern": [{ "pathname": "/foo" }, "https://example.com" ], + "expected_obj": "error" + }, + { + "pattern": [{ "pathname": ":name*" }], + "inputs": [{ "pathname": "foobar" }], + "expected_match": { + "pathname": { "input": "foobar", "groups": { "name": "foobar" }} + } + }, + { + "pattern": [{ "pathname": ":name+" }], + "inputs": [{ "pathname": "foobar" }], + "expected_match": { + "pathname": { "input": "foobar", "groups": { "name": "foobar" }} + } + }, + { + "pattern": [{ "pathname": ":name" }], + "inputs": [{ "pathname": "foobar" }], + "expected_match": { + "pathname": { "input": "foobar", "groups": { "name": "foobar" }} + } + }, + { + "pattern": [{ "protocol": ":name*" }], + "inputs": [{ "protocol": "foobar" }], + "expected_match": { + "protocol": { "input": "foobar", "groups": { "name": "foobar" }} + } + }, + { + "pattern": [{ "protocol": ":name+" }], + "inputs": [{ "protocol": "foobar" }], + "expected_match": { + "protocol": { "input": "foobar", "groups": { "name": "foobar" }} + } + }, + { + "pattern": [{ "protocol": ":name" }], + "inputs": [{ "protocol": "foobar" }], + "expected_match": { + "protocol": { "input": "foobar", "groups": { "name": "foobar" }} + } + }, + { + "pattern": [{ "hostname": "bad hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad#hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad%hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad/hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad\\:hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "badhostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad?hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad@hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad[hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad]hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad\\\\hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad^hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad|hostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad\nhostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad\rhostname" }], + "expected_obj": "error" + }, + { + "pattern": [{ "hostname": "bad\thostname" }], + "expected_obj": "error" + }, + { + "pattern": [{}], + "inputs": ["https://example.com/"], + "expected_match": { + "protocol": { "input": "https", "groups": { "0": "https" }}, + "hostname": { "input": "example.com", "groups": { "0": "example.com" }}, + "pathname": { "input": "/", "groups": { "0": "/" }} + } + }, + { + "pattern": [], + "inputs": ["https://example.com/"], + "expected_match": { + "protocol": { "input": "https", "groups": { "0": "https" }}, + "hostname": { "input": "example.com", "groups": { "0": "example.com" }}, + "pathname": { "input": "/", "groups": { "0": "/" }} + } + }, + { + "pattern": [], + "inputs": [{}], + "expected_match": {} + }, + { + "pattern": [], + "inputs": [], + "expected_match": { "inputs": [{}] } + }, + { + "pattern": [{ "pathname": "(foo)(.*)" }], + "inputs": [{ "pathname": "foobarbaz" }], + "expected_match": { + "pathname": { "input": "foobarbaz", "groups": { "0": "foo", "1": "barbaz" }} + } + }, + { + "pattern": [{ "pathname": "{(foo)bar}(.*)" }], + "inputs": [{ "pathname": "foobarbaz" }], + "expected_match": { + "pathname": { "input": "foobarbaz", "groups": { "0": "foo", "1": "baz" }} + } + }, + { + "pattern": [{ "pathname": "(foo)?(.*)" }], + "inputs": [{ "pathname": "foobarbaz" }], + "expected_obj": { + "pathname": "(foo)?*" + }, + "expected_match": { + "pathname": { "input": "foobarbaz", "groups": { "0": "foo", "1": "barbaz" }} + } + }, + { + "pattern": [{ "pathname": "{:foo}(.*)" }], + "inputs": [{ "pathname": "foobarbaz" }], + "expected_match": { + "pathname": { "input": "foobarbaz", "groups": { "foo": "f", "0": "oobarbaz" }} + } + }, + { + "pattern": [{ "pathname": "{:foo}(barbaz)" }], + "inputs": [{ "pathname": "foobarbaz" }], + "expected_match": { + "pathname": { "input": "foobarbaz", "groups": { "foo": "foo", "0": "barbaz" }} + } + }, + { + "pattern": [{ "pathname": "{:foo}{(.*)}" }], + "inputs": [{ "pathname": "foobarbaz" }], + "expected_obj": { + "pathname": "{:foo}(.*)" + }, + "expected_match": { + "pathname": { "input": "foobarbaz", "groups": { "foo": "f", "0": "oobarbaz" }} + } + }, + { + "pattern": [{ "pathname": "{:foo}{(.*)bar}" }], + "inputs": [{ "pathname": "foobarbaz" }], + "expected_obj": { + "pathname": ":foo{*bar}" + }, + "expected_match": null + }, + { + "pattern": [{ "pathname": "{:foo}{bar(.*)}" }], + "inputs": [{ "pathname": "foobarbaz" }], + "expected_obj": { + "pathname": ":foo{bar*}" + }, + "expected_match": { + "pathname": { "input": "foobarbaz", "groups": { "foo": "foo", "0": "baz" }} + } + }, + { + "pattern": [{ "pathname": "{:foo}:bar(.*)" }], + "inputs": [{ "pathname": "foobarbaz" }], + "expected_obj": { + "pathname": ":foo:bar(.*)" + }, + "expected_match": { + "pathname": { "input": "foobarbaz", "groups": { "foo": "f", "bar": "oobarbaz" }} + } + }, + { + "pattern": [{ "pathname": "{:foo}?(.*)" }], + "inputs": [{ "pathname": "foobarbaz" }], + "expected_obj": { + "pathname": ":foo?*" + }, + "expected_match": { + "pathname": { "input": "foobarbaz", "groups": { "foo": "f", "0": "oobarbaz" }} + } + }, + { + "pattern": [{ "pathname": "{:foo\\bar}" }], + "inputs": [{ "pathname": "foobar" }], + "expected_match": { + "pathname": { "input": "foobar", "groups": { "foo": "foo" }} + } + }, + { + "pattern": [{ "pathname": "{:foo\\.bar}" }], + "inputs": [{ "pathname": "foo.bar" }], + "expected_obj": { + "pathname": "{:foo.bar}" + }, + "expected_match": { + "pathname": { "input": "foo.bar", "groups": { "foo": "foo" }} + } + }, + { + "pattern": [{ "pathname": "{:foo(foo)bar}" }], + "inputs": [{ "pathname": "foobar" }], + "expected_match": { + "pathname": { "input": "foobar", "groups": { "foo": "foo" }} + } + }, + { + "pattern": [{ "pathname": "{:foo}bar" }], + "inputs": [{ "pathname": "foobar" }], + "expected_match": { + "pathname": { "input": "foobar", "groups": { "foo": "foo" }} + } + }, + { + "pattern": [{ "pathname": ":foo\\bar" }], + "inputs": [{ "pathname": "foobar" }], + "expected_obj": { + "pathname": "{:foo}bar" + }, + "expected_match": { + "pathname": { "input": "foobar", "groups": { "foo": "foo" }} + } + }, + { + "pattern": [{ "pathname": ":foo{}(.*)" }], + "inputs": [{ "pathname": "foobar" }], + "expected_obj": { + "pathname": "{:foo}(.*)" + }, + "expected_match": { + "pathname": { "input": "foobar", "groups": { "foo": "f", "0": "oobar" }} + } + }, + { + "pattern": [{ "pathname": ":foo{}bar" }], + "inputs": [{ "pathname": "foobar" }], + "expected_obj": { + "pathname": "{:foo}bar" + }, + "expected_match": { + "pathname": { "input": "foobar", "groups": { "foo": "foo" }} + } + }, + { + "pattern": [{ "pathname": ":foo{}?bar" }], + "inputs": [{ "pathname": "foobar" }], + "expected_obj": { + "pathname": "{:foo}bar" + }, + "expected_match": { + "pathname": { "input": "foobar", "groups": { "foo": "foo" }} + } + }, + { + "pattern": [{ "pathname": "*{}**?" }], + "inputs": [{ "pathname": "foobar" }], + "expected_obj": { + "pathname": "*(.*)?" + }, + "//": "The `null` below is translated to undefined in the test harness.", + "expected_match": { + "pathname": { "input": "foobar", "groups": { "0": "foobar", "1": null }} + } + }, + { + "pattern": [{ "pathname": ":foo(baz)(.*)" }], + "inputs": [{ "pathname": "bazbar" }], + "expected_match": { + "pathname": { "input": "bazbar", "groups": { "foo": "baz", "0": "bar" }} + } + }, + { + "pattern": [{ "pathname": ":foo(baz)bar" }], + "inputs": [{ "pathname": "bazbar" }], + "expected_match": { + "pathname": { "input": "bazbar", "groups": { "foo": "baz" }} + } + }, + { + "pattern": [{ "pathname": "*/*" }], + "inputs": [{ "pathname": "foo/bar" }], + "expected_match": { + "pathname": { "input": "foo/bar", "groups": { "0": "foo", "1": "bar" }} + } + }, + { + "pattern": [{ "pathname": "*\\/*" }], + "inputs": [{ "pathname": "foo/bar" }], + "expected_obj": { + "pathname": "*/{*}" + }, + "expected_match": { + "pathname": { "input": "foo/bar", "groups": { "0": "foo", "1": "bar" }} + } + }, + { + "pattern": [{ "pathname": "*/{*}" }], + "inputs": [{ "pathname": "foo/bar" }], + "expected_match": { + "pathname": { "input": "foo/bar", "groups": { "0": "foo", "1": "bar" }} + } + }, + { + "pattern": [{ "pathname": "*//*" }], + "inputs": [{ "pathname": "foo/bar" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/:foo." }], + "inputs": [{ "pathname": "/bar." }], + "expected_match": { + "pathname": { "input": "/bar.", "groups": { "foo": "bar" } } + } + }, + { + "pattern": [{ "pathname": "/:foo.." }], + "inputs": [{ "pathname": "/bar.." }], + "expected_match": { + "pathname": { "input": "/bar..", "groups": { "foo": "bar" } } + } + }, + { + "pattern": [{ "pathname": "./foo" }], + "inputs": [{ "pathname": "./foo" }], + "expected_match": { + "pathname": { "input": "./foo", "groups": {}} + } + }, + { + "pattern": [{ "pathname": "../foo" }], + "inputs": [{ "pathname": "../foo" }], + "expected_match": { + "pathname": { "input": "../foo", "groups": {}} + } + }, + { + "pattern": [{ "pathname": ":foo./" }], + "inputs": [{ "pathname": "bar./" }], + "expected_match": { + "pathname": { "input": "bar./", "groups": { "foo": "bar" }} + } + }, + { + "pattern": [{ "pathname": ":foo../" }], + "inputs": [{ "pathname": "bar../" }], + "expected_match": { + "pathname": { "input": "bar../", "groups": { "foo": "bar" }} + } + }, + { + "pattern": [{ "pathname": "/:foo\\bar" }], + "inputs": [{ "pathname": "/bazbar" }], + "expected_obj": { + "pathname": "{/:foo}bar" + }, + "expected_match": { + "pathname": { "input": "/bazbar", "groups": { "foo": "baz" }} + } + }, + { + "pattern": [{ "pathname": "/foo/bar" }, { "ignoreCase": true }], + "inputs": [{ "pathname": "/FOO/BAR" }], + "expected_match": { + "pathname": { "input": "/FOO/BAR", "groups": {} } + } + }, + { + "pattern": [{ "ignoreCase": true }], + "inputs": [{ "pathname": "/FOO/BAR" }], + "expected_match": { + "pathname": { "input": "/FOO/BAR", "groups": { "0": "/FOO/BAR" } } + } + }, + { + "pattern": [ "https://example.com:8080/foo?bar#baz", + { "ignoreCase": true }], + "inputs": [{ "pathname": "/FOO", "search": "BAR", "hash": "BAZ", + "baseURL": "https://example.com:8080" }], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "port": "8080", + "pathname": "/foo", + "search": "bar", + "hash": "baz" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "port": { "input": "8080", "groups": {} }, + "pathname": { "input": "/FOO", "groups": {} }, + "search": { "input": "BAR", "groups": {} }, + "hash": { "input": "BAZ", "groups": {} } + } + }, + { + "pattern": [ "/foo?bar#baz", "https://example.com:8080", + { "ignoreCase": true }], + "inputs": [{ "pathname": "/FOO", "search": "BAR", "hash": "BAZ", + "baseURL": "https://example.com:8080" }], + "expected_obj": { + "protocol": "https", + "hostname": "example.com", + "port": "8080", + "pathname": "/foo", + "search": "bar", + "hash": "baz" + }, + "expected_match": { + "protocol": { "input": "https", "groups": {} }, + "hostname": { "input": "example.com", "groups": {} }, + "port": { "input": "8080", "groups": {} }, + "pathname": { "input": "/FOO", "groups": {} }, + "search": { "input": "BAR", "groups": {} }, + "hash": { "input": "BAZ", "groups": {} } + } + }, + { + "pattern": [ "/foo?bar#baz", { "ignoreCase": true }, + "https://example.com:8080" ], + "inputs": [{ "pathname": "/FOO", "search": "BAR", "hash": "BAZ", + "baseURL": "https://example.com:8080" }], + "expected_obj": "error" + }, + { + "pattern": [{ "search": "foo", "baseURL": "https://example.com/a/+/b" }], + "inputs": [{ "search": "foo", "baseURL": "https://example.com/a/+/b" }], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "pathname": "/a/\\+/b" + }, + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/a/+/b", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [{ "hash": "foo", "baseURL": "https://example.com/?q=*&v=?&hmm={}&umm=()" }], + "inputs": [{ "hash": "foo", "baseURL": "https://example.com/?q=*&v=?&hmm={}&umm=()" }], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "search": "q=\\*&v=\\?&hmm=\\{\\}&umm=\\(\\)" + }, + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "q=*&v=?&hmm={}&umm=()", "groups": {} }, + "hash": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [ "#foo", "https://example.com/?q=*&v=?&hmm={}&umm=()" ], + "inputs": [ "https://example.com/?q=*&v=?&hmm={}&umm=()#foo" ], + "exactly_empty_components": [ "port" ], + "expected_obj": { + "search": "q=\\*&v=\\?&hmm=\\{\\}&umm=\\(\\)", + "hash": "foo" + }, + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "q=*&v=?&hmm={}&umm=()", "groups": {} }, + "hash": { "input": "foo", "groups": {} } + } + }, + { + "pattern": [{ "pathname": "/([[a-z]--a])" }], + "inputs": [{ "pathname": "/a" }], + "expected_match": null + }, + { + "pattern": [{ "pathname": "/([[a-z]--a])" }], + "inputs": [{ "pathname": "/z" }], + "expected_match": { + "pathname": { "input": "/z", "groups": { "0": "z" } } + } + }, + { + "pattern": [{ "pathname": "/([\\d&&[0-1]])" }], + "inputs": [{ "pathname": "/0" }], + "expected_match": { + "pathname": { "input": "/0", "groups": { "0": "0" } } + } + }, + { + "pattern": [{ "pathname": "/([\\d&&[0-1]])" }], + "inputs": [{ "pathname": "/3" }], + "expected_match": null + } +] diff --git a/test/fixtures/wpt/urlpattern/resources/urlpatterntests.js b/test/fixtures/wpt/urlpattern/resources/urlpatterntests.js new file mode 100644 index 00000000000000..761f96b6fd3392 --- /dev/null +++ b/test/fixtures/wpt/urlpattern/resources/urlpatterntests.js @@ -0,0 +1,188 @@ +const kComponents = [ + 'protocol', + 'username', + 'password', + 'hostname', + 'port', + 'password', + 'pathname', + 'search', + 'hash', +]; + +function runTests(data) { + for (let entry of data) { + test(function() { + if (entry.expected_obj === 'error') { + assert_throws_js(TypeError, _ => new URLPattern(...entry.pattern), + 'URLPattern() constructor'); + return; + } + + const pattern = new URLPattern(...entry.pattern); + + // If the expected_obj property is not present we will automatically + // fill it with the most likely expected values. + entry.expected_obj = entry.expected_obj || {}; + + // The compiled URLPattern object should have a property for each + // component exposing the compiled pattern string. + for (let component of kComponents) { + // If the test case explicitly provides an expected pattern string, + // then use that. This is necessary in cases where the original + // construction pattern gets canonicalized, etc. + let expected = entry.expected_obj[component]; + + // If there is no explicit expected pattern string, then compute + // the expected value based on the URLPattern constructor args. + if (expected == undefined) { + // First determine if there is a baseURL present in the pattern + // input. A baseURL can be the source for many component patterns. + let baseURL = null; + if (entry.pattern.length > 0 && entry.pattern[0].baseURL) { + baseURL = new URL(entry.pattern[0].baseURL); + } else if (entry.pattern.length > 1 && + typeof entry.pattern[1] === 'string') { + baseURL = new URL(entry.pattern[1]); + } + + const EARLIER_COMPONENTS = { + protocol: [], + hostname: ["protocol"], + port: ["protocol", "hostname"], + username: [], + password: [], + pathname: ["protocol", "hostname", "port"], + search: ["protocol", "hostname", "port", "pathname"], + hash: ["protocol", "hostname", "port", "pathname", "search"], + }; + + // We automatically populate the expected pattern string using + // the following options in priority order: + // + // 1. If the original input explicitly provided a pattern, then + // echo that back as the expected value. + // 2. If an "earlier" component is specified, then a wildcard + // will be used rather than inheriting from the base URL. + // 3. If the baseURL exists and provides a component value then + // use that for the expected pattern. + // 4. Otherwise fall back on the default pattern of `*` for an + // empty component pattern. + // + // Note that username and password are never inherited, and will only + // need to match if explicitly specified. + if (entry.exactly_empty_components && + entry.exactly_empty_components.includes(component)) { + expected = ''; + } else if (typeof entry.pattern[0] === 'object' && + entry.pattern[0][component]) { + expected = entry.pattern[0][component]; + } else if (typeof entry.pattern[0] === 'object' && + EARLIER_COMPONENTS[component].some(c => c in entry.pattern[0])) { + expected = '*'; + } else if (baseURL && component !== 'username' && component !== 'password') { + let base_value = baseURL[component]; + // Unfortunately some URL() getters include separator chars; e.g. + // the trailing `:` for the protocol. Strip those off if necessary. + if (component === 'protocol') + base_value = base_value.substring(0, base_value.length - 1); + else if (component === 'search' || component === 'hash') + base_value = base_value.substring(1, base_value.length); + expected = base_value; + } else { + expected = '*'; + } + } + + // Finally, assert that the compiled object property matches the + // expected property. + assert_equals(pattern[component], expected, + `compiled pattern property '${component}'`); + } + + if (entry.expected_match === 'error') { + assert_throws_js(TypeError, _ => pattern.test(...entry.inputs), + 'test() result'); + assert_throws_js(TypeError, _ => pattern.exec(...entry.inputs), + 'exec() result'); + return; + } + + // First, validate the test() method by converting the expected result to + // a truthy value. + assert_equals(pattern.test(...entry.inputs), !!entry.expected_match, + 'test() result'); + + // Next, start validating the exec() method. + const exec_result = pattern.exec(...entry.inputs); + + // On a failed match exec() returns null. + if (!entry.expected_match || typeof entry.expected_match !== "object") { + assert_equals(exec_result, entry.expected_match, 'exec() failed match result'); + return; + } + + if (!entry.expected_match.inputs) + entry.expected_match.inputs = entry.inputs; + + // Next verify the result.input is correct. This may be a structured + // URLPatternInit dictionary object or a URL string. + assert_equals(exec_result.inputs.length, + entry.expected_match.inputs.length, + 'exec() result.inputs.length'); + for (let i = 0; i < exec_result.inputs.length; ++i) { + const input = exec_result.inputs[i]; + const expected_input = entry.expected_match.inputs[i]; + if (typeof input === 'string') { + assert_equals(input, expected_input, `exec() result.inputs[${i}]`); + continue; + } + for (let component of kComponents) { + assert_equals(input[component], expected_input[component], + `exec() result.inputs[${i}][${component}]`); + } + } + + // Next we will compare the URLPatternComponentResult for each of these + // expected components. + for (let component of kComponents) { + let expected_obj = entry.expected_match[component]; + + // If the test expectations don't include a component object, then + // we auto-generate one. This is convenient for the many cases + // where the pattern has a default wildcard or empty string pattern + // for a component and the input is essentially empty. + if (!expected_obj) { + expected_obj = { input: '', groups: {} }; + + // Next, we must treat default wildcards differently than empty string + // patterns. The wildcard results in a capture group, but the empty + // string pattern does not. The expectation object must list which + // components should be empty instead of wildcards in + // |exactly_empty_components|. + if (!entry.exactly_empty_components || + !entry.exactly_empty_components.includes(component)) { + expected_obj.groups['0'] = ''; + } + } + // JSON does not allow us to use undefined directly, so the data file + // contains null instead. Translate to the expected undefined value + // here. + for (const key in expected_obj.groups) { + if (expected_obj.groups[key] === null) { + expected_obj.groups[key] = undefined; + } + } + assert_object_equals(exec_result[component], expected_obj, + `exec() result for ${component}`); + } + }, `Pattern: ${JSON.stringify(entry.pattern)} ` + + `Inputs: ${JSON.stringify(entry.inputs)}`); + } +} + +promise_test(async function() { + const response = await fetch('resources/urlpatterntestdata.json'); + const data = await response.json(); + runTests(data); +}, 'Loading data...'); diff --git a/test/fixtures/wpt/urlpattern/urlpattern-compare.tentative.any.js b/test/fixtures/wpt/urlpattern/urlpattern-compare.tentative.any.js new file mode 100644 index 00000000000000..91e9f24f850b13 --- /dev/null +++ b/test/fixtures/wpt/urlpattern/urlpattern-compare.tentative.any.js @@ -0,0 +1,2 @@ +// META: global=window,worker +// META: script=resources/urlpattern-compare-tests.tentative.js diff --git a/test/fixtures/wpt/urlpattern/urlpattern-compare.tentative.https.any.js b/test/fixtures/wpt/urlpattern/urlpattern-compare.tentative.https.any.js new file mode 100644 index 00000000000000..91e9f24f850b13 --- /dev/null +++ b/test/fixtures/wpt/urlpattern/urlpattern-compare.tentative.https.any.js @@ -0,0 +1,2 @@ +// META: global=window,worker +// META: script=resources/urlpattern-compare-tests.tentative.js diff --git a/test/fixtures/wpt/urlpattern/urlpattern-hasregexpgroups.any.js b/test/fixtures/wpt/urlpattern/urlpattern-hasregexpgroups.any.js new file mode 100644 index 00000000000000..33133d2511b065 --- /dev/null +++ b/test/fixtures/wpt/urlpattern/urlpattern-hasregexpgroups.any.js @@ -0,0 +1,2 @@ +// META: global=window,worker +// META: script=resources/urlpattern-hasregexpgroups-tests.js diff --git a/test/fixtures/wpt/urlpattern/urlpattern.any.js b/test/fixtures/wpt/urlpattern/urlpattern.any.js new file mode 100644 index 00000000000000..7d47d22609bf8e --- /dev/null +++ b/test/fixtures/wpt/urlpattern/urlpattern.any.js @@ -0,0 +1,2 @@ +// META: global=window,worker +// META: script=resources/urlpatterntests.js diff --git a/test/fixtures/wpt/urlpattern/urlpattern.https.any.js b/test/fixtures/wpt/urlpattern/urlpattern.https.any.js new file mode 100644 index 00000000000000..7d47d22609bf8e --- /dev/null +++ b/test/fixtures/wpt/urlpattern/urlpattern.https.any.js @@ -0,0 +1,2 @@ +// META: global=window,worker +// META: script=resources/urlpatterntests.js diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index 2560056dba990d..1ab3619ec85c8f 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -75,6 +75,10 @@ "commit": "6fa3fe8a929be45422cd46a8961e647e13d0cab8", "path": "url" }, + "urlpattern": { + "commit": "1b56d89a261b86dedfd2854b53c1732e435f1f57", + "path": "urlpattern" + }, "user-timing": { "commit": "5ae85bf8267ac617833dc013dee9774c9e2a18b7", "path": "user-timing" diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index c75ee390dcd195..bdaf12df02e58d 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -74,6 +74,7 @@ expected.beforePreExec = new Set([ 'NativeModule internal/querystring', 'NativeModule querystring', 'Internal Binding url', + 'Internal Binding url_pattern', 'Internal Binding blob', 'NativeModule internal/url', 'NativeModule util', diff --git a/test/wpt/status/url.json b/test/wpt/status/url.json index d361048874d72d..0d9e23893d0597 100644 --- a/test/wpt/status/url.json +++ b/test/wpt/status/url.json @@ -1,7 +1,4 @@ { - "idlharness-shadowrealm.window.js": { - "skip": "ShadowRealm support is not enabled" - }, "percent-encoding.window.js": { "skip": "TODO: port from .window.js" }, @@ -19,13 +16,7 @@ "expected": [ "\uD87E\uDC68.com (using URL)", "\uD87E\uDC68.com (using URL.host)", - "\uD87E\uDC68.com (using URL.hostname)", - "\u1E9E.com (using URL)", - "\u1E9E.com (using URL.host)", - "\u1E9E.com (using URL.hostname)", - "\u1E9E.foo.com (using URL)", - "\u1E9E.foo.com (using URL.host)", - "\u1E9E.foo.com (using URL.hostname)" + "\uD87E\uDC68.com (using URL.hostname)" ] } }, diff --git a/test/wpt/status/urlpattern.json b/test/wpt/status/urlpattern.json new file mode 100644 index 00000000000000..b8c2c68a3a54a1 --- /dev/null +++ b/test/wpt/status/urlpattern.json @@ -0,0 +1,169 @@ +{ + "resources/urlpattern-compare-tests.tentative.js": { + "skip": "compareComponent function is not yet included in the URLPattern spec" + }, + "urlpattern-compare.tentative.any.js": { + "skip": "compareComponent function is not yet included in the URLPattern spec" + }, + "urlpattern-compare.tentative.https.any.js": { + "skip": "compareComponent function is not yet included in the URLPattern spec" + }, + "urlpattern.any.js": { + "fail": { + "expected": [ + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [{\"protocol\":\"https\",\"hostname\":\"example.com\",\"pathname\":\"/foo/bar\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com\"}] Inputs: [{\"protocol\":\"https\",\"hostname\":\"example.com\",\"pathname\":\"/foo/bar\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [{\"protocol\":\"https\",\"hostname\":\"example.com\",\"pathname\":\"/foo/bar\",\"search\":\"otherquery\",\"hash\":\"otherhash\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com\"}] Inputs: [{\"protocol\":\"https\",\"hostname\":\"example.com\",\"pathname\":\"/foo/bar\",\"search\":\"otherquery\",\"hash\":\"otherhash\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?otherquery#otherhash\"}] Inputs: [{\"protocol\":\"https\",\"hostname\":\"example.com\",\"pathname\":\"/foo/bar\",\"search\":\"otherquery\",\"hash\":\"otherhash\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [\"https://example.com/foo/bar\"]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [\"https://example.com/foo/bar?otherquery#otherhash\"]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [\"https://example.com/foo/bar?query#hash\"]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}]", + "Pattern: [{\"pathname\":\"/foo/:bar?\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"pathname\":\"/foo/:bar*\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"pathname\":\"/foo/(.*)?\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"pathname\":\"/foo/*?\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"pathname\":\"/foo/(.*)*\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"pathname\":\"/foo/**\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"port\":\"\"}] Inputs: [{\"protocol\":\"http\",\"port\":\"80\"}]", + "Pattern: [{\"protocol\":\"http\",\"port\":\"80\"}] Inputs: [{\"protocol\":\"http\",\"port\":\"80\"}]", + "Pattern: [{\"protocol\":\"http\",\"port\":\"80 \"}] Inputs: [{\"protocol\":\"http\",\"port\":\"80\"}]", + "Pattern: [{\"pathname\":\"./foo/bar\",\"baseURL\":\"https://example.com\"}] Inputs: [{\"pathname\":\"foo/bar\",\"baseURL\":\"https://example.com\"}]", + "Pattern: [{\"pathname\":\"\",\"baseURL\":\"https://example.com\"}] Inputs: [{\"pathname\":\"/\",\"baseURL\":\"https://example.com\"}]", + "Pattern: [{\"pathname\":\"b\",\"baseURL\":\"https://example.com/foo/\"}] Inputs: [{\"pathname\":\"./b\",\"baseURL\":\"https://example.com/foo/\"}]", + "Pattern: [{\"pathname\":\"foo/bar\",\"baseURL\":\"https://example.com\"}] Inputs: [\"https://example.com/foo/bar\"]", + "Pattern: [{\"pathname\":\":name.html\",\"baseURL\":\"https://example.com\"}] Inputs: [\"https://example.com/foo.html\"]", + "Pattern: [{\"pathname\":\"/(\\\\m)\"}] Inputs: undefined", + "Pattern: [\"http{s}?://{*.}?example.com/:product/:endpoint\"] Inputs: [\"https://sub.example.com/foo/bar\"]", + "Pattern: [\"https://example.com?foo\"] Inputs: [\"https://example.com/?foo\"]", + "Pattern: [\"https://example.com#foo\"] Inputs: [\"https://example.com/#foo\"]", + "Pattern: [\"https://example.com:8080#foo\"] Inputs: [\"https://example.com:8080/#foo\"]", + "Pattern: [\"https://example.com/?foo\"] Inputs: [\"https://example.com/?foo\"]", + "Pattern: [\"https://example.com/#foo\"] Inputs: [\"https://example.com/#foo\"]", + "Pattern: [\"https://example.com/*\\\\?foo\"] Inputs: [\"https://example.com/?foo\"]", + "Pattern: [\"https://example.com/:name\\\\?foo\"] Inputs: [\"https://example.com/bar?foo\"]", + "Pattern: [\"https://example.com/(bar)\\\\?foo\"] Inputs: [\"https://example.com/bar?foo\"]", + "Pattern: [\"https://example.com/{bar}\\\\?foo\"] Inputs: [\"https://example.com/bar?foo\"]", + "Pattern: [\"data\\\\:foobar\"] Inputs: [\"data:foobar\"]", + "Pattern: [\"https://{sub.}?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", + "Pattern: [\"https://{sub.}?example{.com/}foo\"] Inputs: [\"https://example.com/foo\"]", + "Pattern: [\"https://(sub.)?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", + "Pattern: [\"https://(sub(?:.))?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", + "Pattern: [\"file:///foo/bar\"] Inputs: [\"file:///foo/bar\"]", + "Pattern: [\"data:\"] Inputs: [\"data:\"]", + "Pattern: [\"?bar#baz\",\"https://example.com/foo\"] Inputs: [\"?bar#baz\",\"https://example.com/foo\"]", + "Pattern: [\"?bar\",\"https://example.com/foo#baz\"] Inputs: [\"?bar\",\"https://example.com/foo#snafu\"]", + "Pattern: [\"#baz\",\"https://example.com/foo?bar\"] Inputs: [\"#baz\",\"https://example.com/foo?bar\"]", + "Pattern: [\"#baz\",\"https://example.com/foo\"] Inputs: [\"#baz\",\"https://example.com/foo\"]", + "Pattern: [\"https://foo\\\\:bar@example.com\"] Inputs: [\"https://foo:bar@example.com\"]", + "Pattern: [\"https://foo@example.com\"] Inputs: [\"https://foo@example.com\"]", + "Pattern: [\"https://\\\\:bar@example.com\"] Inputs: [\"https://:bar@example.com\"]", + "Pattern: [\"https://:user::pass@example.com\"] Inputs: [\"https://foo:bar@example.com\"]", + "Pattern: [\"https\\\\:foo\\\\:bar@example.com\"] Inputs: [\"https:foo:bar@example.com\"]", + "Pattern: [\"data\\\\:foo\\\\:bar@example.com\"] Inputs: [\"data:foo:bar@example.com\"]", + "Pattern: [\"data{\\\\:}channel.html\",\"https://example.com\"] Inputs: [\"https://example.com/data:channel.html\"]", + "Pattern: [\"http://[\\\\:\\\\:1]/\"] Inputs: [\"http://[::1]/\"]", + "Pattern: [\"http://[\\\\:\\\\:a]/\"] Inputs: [\"http://[::a]/\"]", + "Pattern: [\"http://[:address]/\"] Inputs: [\"http://[::1]/\"]", + "Pattern: [\"http://[\\\\:\\\\:AB\\\\::num]/\"] Inputs: [\"http://[::ab:1]/\"]", + "Pattern: [\"data\\\\:text/javascript,let x = 100/:tens?5;\"] Inputs: [\"data:text/javascript,let x = 100/5;\"]", + "Pattern: [{\"hostname\":\"bad#hostname\"}] Inputs: undefined", + "Pattern: [{\"hostname\":\"bad/hostname\"}] Inputs: undefined", + "Pattern: [{\"hostname\":\"bad\\\\\\\\hostname\"}] Inputs: undefined", + "Pattern: [{\"hostname\":\"bad\\nhostname\"}] Inputs: undefined", + "Pattern: [{\"hostname\":\"bad\\rhostname\"}] Inputs: undefined", + "Pattern: [{\"hostname\":\"bad\\thostname\"}] Inputs: undefined", + "Pattern: [] Inputs: []", + "Pattern: [{\"pathname\":\"*{}**?\"}] Inputs: [{\"pathname\":\"foobar\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\"},{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO/BAR\"}]", + "Pattern: [\"https://example.com:8080/foo?bar#baz\",{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO\",\"search\":\"BAR\",\"hash\":\"BAZ\",\"baseURL\":\"https://example.com:8080\"}]", + "Pattern: [{\"search\":\"foo\",\"baseURL\":\"https://example.com/a/+/b\"}] Inputs: [{\"search\":\"foo\",\"baseURL\":\"https://example.com/a/+/b\"}]", + "Pattern: [{\"hash\":\"foo\",\"baseURL\":\"https://example.com/?q=*&v=?&hmm={}&umm=()\"}] Inputs: [{\"hash\":\"foo\",\"baseURL\":\"https://example.com/?q=*&v=?&hmm={}&umm=()\"}]", + "Pattern: [\"#foo\",\"https://example.com/?q=*&v=?&hmm={}&umm=()\"] Inputs: [\"https://example.com/?q=*&v=?&hmm={}&umm=()#foo\"]", + "Pattern: [{\"pathname\":\"/([[a-z]--a])\"}] Inputs: [{\"pathname\":\"/z\"}]", + "Pattern: [{\"pathname\":\"/([\\\\d&&[0-1]])\"}] Inputs: [{\"pathname\":\"/0\"}]" + ] + } + }, + "urlpattern.https.any.js": { + "fail": { + "expected": [ + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [{\"protocol\":\"https\",\"hostname\":\"example.com\",\"pathname\":\"/foo/bar\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com\"}] Inputs: [{\"protocol\":\"https\",\"hostname\":\"example.com\",\"pathname\":\"/foo/bar\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [{\"protocol\":\"https\",\"hostname\":\"example.com\",\"pathname\":\"/foo/bar\",\"search\":\"otherquery\",\"hash\":\"otherhash\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com\"}] Inputs: [{\"protocol\":\"https\",\"hostname\":\"example.com\",\"pathname\":\"/foo/bar\",\"search\":\"otherquery\",\"hash\":\"otherhash\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?otherquery#otherhash\"}] Inputs: [{\"protocol\":\"https\",\"hostname\":\"example.com\",\"pathname\":\"/foo/bar\",\"search\":\"otherquery\",\"hash\":\"otherhash\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [\"https://example.com/foo/bar\"]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [\"https://example.com/foo/bar?otherquery#otherhash\"]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [\"https://example.com/foo/bar?query#hash\"]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}] Inputs: [{\"pathname\":\"/foo/bar\",\"baseURL\":\"https://example.com?query#hash\"}]", + "Pattern: [{\"pathname\":\"/foo/:bar?\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"pathname\":\"/foo/:bar*\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"pathname\":\"/foo/(.*)?\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"pathname\":\"/foo/*?\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"pathname\":\"/foo/(.*)*\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"pathname\":\"/foo/**\"}] Inputs: [{\"pathname\":\"/foo\"}]", + "Pattern: [{\"port\":\"\"}] Inputs: [{\"protocol\":\"http\",\"port\":\"80\"}]", + "Pattern: [{\"protocol\":\"http\",\"port\":\"80\"}] Inputs: [{\"protocol\":\"http\",\"port\":\"80\"}]", + "Pattern: [{\"protocol\":\"http\",\"port\":\"80 \"}] Inputs: [{\"protocol\":\"http\",\"port\":\"80\"}]", + "Pattern: [{\"pathname\":\"./foo/bar\",\"baseURL\":\"https://example.com\"}] Inputs: [{\"pathname\":\"foo/bar\",\"baseURL\":\"https://example.com\"}]", + "Pattern: [{\"pathname\":\"\",\"baseURL\":\"https://example.com\"}] Inputs: [{\"pathname\":\"/\",\"baseURL\":\"https://example.com\"}]", + "Pattern: [{\"pathname\":\"b\",\"baseURL\":\"https://example.com/foo/\"}] Inputs: [{\"pathname\":\"./b\",\"baseURL\":\"https://example.com/foo/\"}]", + "Pattern: [{\"pathname\":\"foo/bar\",\"baseURL\":\"https://example.com\"}] Inputs: [\"https://example.com/foo/bar\"]", + "Pattern: [{\"pathname\":\":name.html\",\"baseURL\":\"https://example.com\"}] Inputs: [\"https://example.com/foo.html\"]", + "Pattern: [{\"pathname\":\"/(\\\\m)\"}] Inputs: undefined", + "Pattern: [\"http{s}?://{*.}?example.com/:product/:endpoint\"] Inputs: [\"https://sub.example.com/foo/bar\"]", + "Pattern: [\"https://example.com?foo\"] Inputs: [\"https://example.com/?foo\"]", + "Pattern: [\"https://example.com#foo\"] Inputs: [\"https://example.com/#foo\"]", + "Pattern: [\"https://example.com:8080#foo\"] Inputs: [\"https://example.com:8080/#foo\"]", + "Pattern: [\"https://example.com/?foo\"] Inputs: [\"https://example.com/?foo\"]", + "Pattern: [\"https://example.com/#foo\"] Inputs: [\"https://example.com/#foo\"]", + "Pattern: [\"https://example.com/*\\\\?foo\"] Inputs: [\"https://example.com/?foo\"]", + "Pattern: [\"https://example.com/:name\\\\?foo\"] Inputs: [\"https://example.com/bar?foo\"]", + "Pattern: [\"https://example.com/(bar)\\\\?foo\"] Inputs: [\"https://example.com/bar?foo\"]", + "Pattern: [\"https://example.com/{bar}\\\\?foo\"] Inputs: [\"https://example.com/bar?foo\"]", + "Pattern: [\"data\\\\:foobar\"] Inputs: [\"data:foobar\"]", + "Pattern: [\"https://{sub.}?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", + "Pattern: [\"https://{sub.}?example{.com/}foo\"] Inputs: [\"https://example.com/foo\"]", + "Pattern: [\"https://(sub.)?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", + "Pattern: [\"https://(sub(?:.))?example.com/foo\"] Inputs: [\"https://example.com/foo\"]", + "Pattern: [\"file:///foo/bar\"] Inputs: [\"file:///foo/bar\"]", + "Pattern: [\"data:\"] Inputs: [\"data:\"]", + "Pattern: [\"?bar#baz\",\"https://example.com/foo\"] Inputs: [\"?bar#baz\",\"https://example.com/foo\"]", + "Pattern: [\"?bar\",\"https://example.com/foo#baz\"] Inputs: [\"?bar\",\"https://example.com/foo#snafu\"]", + "Pattern: [\"#baz\",\"https://example.com/foo?bar\"] Inputs: [\"#baz\",\"https://example.com/foo?bar\"]", + "Pattern: [\"#baz\",\"https://example.com/foo\"] Inputs: [\"#baz\",\"https://example.com/foo\"]", + "Pattern: [\"https://foo\\\\:bar@example.com\"] Inputs: [\"https://foo:bar@example.com\"]", + "Pattern: [\"https://foo@example.com\"] Inputs: [\"https://foo@example.com\"]", + "Pattern: [\"https://\\\\:bar@example.com\"] Inputs: [\"https://:bar@example.com\"]", + "Pattern: [\"https://:user::pass@example.com\"] Inputs: [\"https://foo:bar@example.com\"]", + "Pattern: [\"https\\\\:foo\\\\:bar@example.com\"] Inputs: [\"https:foo:bar@example.com\"]", + "Pattern: [\"data\\\\:foo\\\\:bar@example.com\"] Inputs: [\"data:foo:bar@example.com\"]", + "Pattern: [\"data{\\\\:}channel.html\",\"https://example.com\"] Inputs: [\"https://example.com/data:channel.html\"]", + "Pattern: [\"http://[\\\\:\\\\:1]/\"] Inputs: [\"http://[::1]/\"]", + "Pattern: [\"http://[\\\\:\\\\:a]/\"] Inputs: [\"http://[::a]/\"]", + "Pattern: [\"http://[:address]/\"] Inputs: [\"http://[::1]/\"]", + "Pattern: [\"http://[\\\\:\\\\:AB\\\\::num]/\"] Inputs: [\"http://[::ab:1]/\"]", + "Pattern: [\"data\\\\:text/javascript,let x = 100/:tens?5;\"] Inputs: [\"data:text/javascript,let x = 100/5;\"]", + "Pattern: [{\"hostname\":\"bad#hostname\"}] Inputs: undefined", + "Pattern: [{\"hostname\":\"bad/hostname\"}] Inputs: undefined", + "Pattern: [{\"hostname\":\"bad\\\\\\\\hostname\"}] Inputs: undefined", + "Pattern: [{\"hostname\":\"bad\\nhostname\"}] Inputs: undefined", + "Pattern: [{\"hostname\":\"bad\\rhostname\"}] Inputs: undefined", + "Pattern: [{\"hostname\":\"bad\\thostname\"}] Inputs: undefined", + "Pattern: [{\"pathname\":\"*{}**?\"}] Inputs: [{\"pathname\":\"foobar\"}]", + "Pattern: [{\"pathname\":\"/foo/bar\"},{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO/BAR\"}]", + "Pattern: [\"https://example.com:8080/foo?bar#baz\",{\"ignoreCase\":true}] Inputs: [{\"pathname\":\"/FOO\",\"search\":\"BAR\",\"hash\":\"BAZ\",\"baseURL\":\"https://example.com:8080\"}]", + "Pattern: [{\"search\":\"foo\",\"baseURL\":\"https://example.com/a/+/b\"}] Inputs: [{\"search\":\"foo\",\"baseURL\":\"https://example.com/a/+/b\"}]", + "Pattern: [{\"hash\":\"foo\",\"baseURL\":\"https://example.com/?q=*&v=?&hmm={}&umm=()\"}] Inputs: [{\"hash\":\"foo\",\"baseURL\":\"https://example.com/?q=*&v=?&hmm={}&umm=()\"}]", + "Pattern: [\"#foo\",\"https://example.com/?q=*&v=?&hmm={}&umm=()\"] Inputs: [\"https://example.com/?q=*&v=?&hmm={}&umm=()#foo\"]", + "Pattern: [{\"pathname\":\"/([[a-z]--a])\"}] Inputs: [{\"pathname\":\"/z\"}]", + "Pattern: [{\"pathname\":\"/([\\\\d&&[0-1]])\"}] Inputs: [{\"pathname\":\"/0\"}]", + "Pattern: [] Inputs: []" + ] + } + } +} \ No newline at end of file diff --git a/test/wpt/test-urlpattern.js b/test/wpt/test-urlpattern.js new file mode 100644 index 00000000000000..e024e5f8feae2a --- /dev/null +++ b/test/wpt/test-urlpattern.js @@ -0,0 +1,8 @@ +'use strict'; + +const { WPTRunner } = require('../common/wpt'); + +const runner = new WPTRunner('urlpattern'); + +runner.pretendGlobalThisAs('Window'); +runner.runJsTests(); diff --git a/typings/globals.d.ts b/typings/globals.d.ts index b37ce9428fd8f2..6e13d589f62c69 100644 --- a/typings/globals.d.ts +++ b/typings/globals.d.ts @@ -14,6 +14,7 @@ import { SymbolsBinding } from './internalBinding/symbols'; import { TimersBinding } from './internalBinding/timers'; import { TypesBinding } from './internalBinding/types'; import { URLBinding } from './internalBinding/url'; +import { URLPatternBinding } from "./internalBinding/url_pattern"; import { UtilBinding } from './internalBinding/util'; import { WASIBinding } from './internalBinding/wasi'; import { WorkerBinding } from './internalBinding/worker'; @@ -38,6 +39,7 @@ interface InternalBindingMap { timers: TimersBinding; types: TypesBinding; url: URLBinding; + url_pattern: URLPatternBinding; util: UtilBinding; wasi: WASIBinding; worker: WorkerBinding; diff --git a/typings/internalBinding/url_pattern.d.ts b/typings/internalBinding/url_pattern.d.ts new file mode 100644 index 00000000000000..516d34d5141f9e --- /dev/null +++ b/typings/internalBinding/url_pattern.d.ts @@ -0,0 +1,20 @@ +export class URLPattern { + protocol: string + username: string + password: string + hostname: string + port: string + pathname: string + search: string + hash: string + + constructor(input: Record | string, options?: { ignoreCase: boolean }); + constructor(input: Record | string, baseUrl?: string, options?: { ignoreCase: boolean }); + + exec(input: string | Record, baseURL?: string): null | Record; + test(input: string | Record, baseURL?: string): boolean; +} + +export interface URLPatternBinding { + URLPattern: URLPattern; +}