Ada 2.7.8
Fast spec-compliant URL parser
Loading...
Searching...
No Matches
helpers.cpp
Go to the documentation of this file.
1#include "ada.h"
2#include "ada/checkers-inl.h"
3#include "ada/checkers.h"
4#include "ada/common_defs.h" // make sure ADA_IS_BIG_ENDIAN gets defined.
5#include "ada/unicode.h"
6#include "ada/scheme.h"
7
8#include <algorithm>
9#include <charconv>
10#include <cstring>
11#include <sstream>
12
13namespace ada::helpers {
14
15template <typename out_iter>
16void encode_json(std::string_view view, out_iter out) {
17 // trivial implementation. could be faster.
18 const char* hexvalues =
19 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
20 for (uint8_t c : view) {
21 if (c == '\\') {
22 *out++ = '\\';
23 *out++ = '\\';
24 } else if (c == '"') {
25 *out++ = '\\';
26 *out++ = '"';
27 } else if (c <= 0x1f) {
28 *out++ = '\\';
29 *out++ = 'u';
30 *out++ = '0';
31 *out++ = '0';
32 *out++ = hexvalues[2 * c];
33 *out++ = hexvalues[2 * c + 1];
34 } else {
35 *out++ = c;
36 }
37 }
38}
39
41 switch (s) {
43 return "Authority";
45 return "Scheme Start";
47 return "Scheme";
49 return "Host";
51 return "No Scheme";
53 return "Fragment";
55 return "Relative Scheme";
57 return "Relative Slash";
59 return "File";
61 return "File Host";
63 return "File Slash";
65 return "Path or Authority";
67 return "Special Authority Ignore Slashes";
69 return "Special Authority Slashes";
71 return "Special Relative or Authority";
73 return "Query";
75 return "Path";
77 return "Path Start";
79 return "Opaque Path";
81 return "Port";
82 default:
83 return "unknown state";
84 }
85}
86
87ada_really_inline std::optional<std::string_view> prune_hash(
88 std::string_view& input) noexcept {
89 // compiles down to 20--30 instructions including a class to memchr (C
90 // function). this function should be quite fast.
91 size_t location_of_first = input.find('#');
92 if (location_of_first == std::string_view::npos) {
93 return std::nullopt;
94 }
95 std::string_view hash = input;
96 hash.remove_prefix(location_of_first + 1);
97 input.remove_suffix(input.size() - location_of_first);
98 return hash;
99}
100
101ada_really_inline bool shorten_path(std::string& path,
102 ada::scheme::type type) noexcept {
103 size_t first_delimiter = path.find_first_of('/', 1);
104
105 // Let path be url's path.
106 // If url's scheme is "file", path's size is 1, and path[0] is a normalized
107 // Windows drive letter, then return.
108 if (type == ada::scheme::type::FILE &&
109 first_delimiter == std::string_view::npos && !path.empty()) {
111 helpers::substring(path, 1))) {
112 return false;
113 }
114 }
115
116 // Remove path's last item, if any.
117 size_t last_delimiter = path.rfind('/');
118 if (last_delimiter != std::string::npos) {
119 path.erase(last_delimiter);
120 return true;
121 }
122
123 return false;
124}
125
126ada_really_inline bool shorten_path(std::string_view& path,
127 ada::scheme::type type) noexcept {
128 size_t first_delimiter = path.find_first_of('/', 1);
129
130 // Let path be url's path.
131 // If url's scheme is "file", path's size is 1, and path[0] is a normalized
132 // Windows drive letter, then return.
133 if (type == ada::scheme::type::FILE &&
134 first_delimiter == std::string_view::npos && !path.empty()) {
136 helpers::substring(path, 1))) {
137 return false;
138 }
139 }
140
141 // Remove path's last item, if any.
142 if (!path.empty()) {
143 size_t slash_loc = path.rfind('/');
144 if (slash_loc != std::string_view::npos) {
145 path.remove_suffix(path.size() - slash_loc);
146 return true;
147 }
148 }
149
150 return false;
151}
152
153ada_really_inline void remove_ascii_tab_or_newline(
154 std::string& input) noexcept {
155 // if this ever becomes a performance issue, we could use an approach similar
156 // to has_tabs_or_newline
157 input.erase(std::remove_if(input.begin(), input.end(),
158 [](char c) {
159 return ada::unicode::is_ascii_tab_or_newline(c);
160 }),
161 input.end());
162}
163
164ada_really_inline std::string_view substring(std::string_view input,
165 size_t pos) noexcept {
166 ADA_ASSERT_TRUE(pos <= input.size());
167 // The following is safer but unneeded if we have the above line:
168 // return pos > input.size() ? std::string_view() : input.substr(pos);
169 return input.substr(pos);
170}
171
172ada_really_inline void resize(std::string_view& input, size_t pos) noexcept {
173 ADA_ASSERT_TRUE(pos <= input.size());
174 input.remove_suffix(input.size() - pos);
175}
176
177// computes the number of trailing zeroes
178// this is a private inline function only defined in this source file.
180#ifdef ADA_REGULAR_VISUAL_STUDIO
181 unsigned long ret;
182 // Search the mask data from least significant bit (LSB)
183 // to the most significant bit (MSB) for a set bit (1).
185 return (int)ret;
186#else // ADA_REGULAR_VISUAL_STUDIO
188#endif // ADA_REGULAR_VISUAL_STUDIO
189}
190
191// starting at index location, this finds the next location of a character
192// :, /, \\, ? or [. If none is found, view.size() is returned.
193// For use within get_host_delimiter_location.
194#if ADA_NEON
195// The ada_make_uint8x16_t macro is necessary because Visual Studio does not
196// support direct initialization of uint8x16_t. See
197// https://developercommunity.visualstudio.com/t/error-C2078:-too-many-initializers-whe/402911?q=backend+neon
198#ifndef ada_make_uint8x16_t
199#define ada_make_uint8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
200 x13, x14, x15, x16) \
201 ([=]() { \
202 static uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
203 x9, x10, x11, x12, x13, x14, x15, x16}; \
204 return vld1q_u8(array); \
205 }())
206#endif
207
209 std::string_view view, size_t location) noexcept {
210 // first check for short strings in which case we do it naively.
211 if (view.size() - location < 16) { // slow path
212 for (size_t i = location; i < view.size(); i++) {
213 if (view[i] == ':' || view[i] == '/' || view[i] == '\\' ||
214 view[i] == '?' || view[i] == '[') {
215 return i;
216 }
217 }
218 return size_t(view.size());
219 }
220 auto to_bitmask = [](uint8x16_t input) -> uint16_t {
222 ada_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01,
223 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
226 tmp = vpaddq_u8(tmp, tmp);
227 tmp = vpaddq_u8(tmp, tmp);
229 };
230
231 // fast path for long strings (expected to be common)
232 size_t i = location;
234 ada_make_uint8x16_t(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x03);
237 ada_make_uint8x16_t(0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
240 uint8x16_t zero{0};
241 for (; i + 15 < view.size(); i += 16) {
242 uint8x16_t word = vld1q_u8((const uint8_t*)view.data() + i);
246 if (vmaxvq_u8(classify) != 0) {
249 return i + trailing_zeroes(is_non_zero);
250 }
251 }
252
253 if (i < view.size()) {
255 vld1q_u8((const uint8_t*)view.data() + view.length() - 16);
259 if (vmaxvq_u8(classify) != 0) {
262 return view.length() - 16 + trailing_zeroes(is_non_zero);
263 }
264 }
265 return size_t(view.size());
266}
267#elif ADA_SSE2
269 std::string_view view, size_t location) noexcept {
270 // first check for short strings in which case we do it naively.
271 if (view.size() - location < 16) { // slow path
272 for (size_t i = location; i < view.size(); i++) {
273 if (view[i] == ':' || view[i] == '/' || view[i] == '\\' ||
274 view[i] == '?' || view[i] == '[') {
275 return i;
276 }
277 }
278 return size_t(view.size());
279 }
280 // fast path for long strings (expected to be common)
281 size_t i = location;
282 const __m128i mask1 = _mm_set1_epi8(':');
283 const __m128i mask2 = _mm_set1_epi8('/');
284 const __m128i mask3 = _mm_set1_epi8('\\');
285 const __m128i mask4 = _mm_set1_epi8('?');
286 const __m128i mask5 = _mm_set1_epi8('[');
287
288 for (; i + 15 < view.size(); i += 16) {
289 __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
297 int mask = _mm_movemask_epi8(m);
298 if (mask != 0) {
299 return i + trailing_zeroes(mask);
300 }
301 }
302 if (i < view.size()) {
303 __m128i word =
304 _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
312 int mask = _mm_movemask_epi8(m);
313 if (mask != 0) {
314 return view.length() - 16 + trailing_zeroes(mask);
315 }
316 }
317 return size_t(view.length());
318}
319#else
320// : / [ \\ ?
321static constexpr std::array<uint8_t, 256> special_host_delimiters =
322 []() constexpr {
323 std::array<uint8_t, 256> result{};
324 for (int i : {':', '/', '[', '\\', '?'}) {
325 result[i] = 1;
326 }
327 return result;
328 }();
329// credit: @the-moisrex recommended a table-based approach
331 std::string_view view, size_t location) noexcept {
332 auto const str = view.substr(location);
333 for (auto pos = str.begin(); pos != str.end(); ++pos) {
334 if (special_host_delimiters[(uint8_t)*pos]) {
335 return pos - str.begin() + location;
336 }
337 }
338 return size_t(view.size());
339}
340#endif
341
342// starting at index location, this finds the next location of a character
343// :, /, ? or [. If none is found, view.size() is returned.
344// For use within get_host_delimiter_location.
345#if ADA_NEON
346ada_really_inline size_t find_next_host_delimiter(std::string_view view,
347 size_t location) noexcept {
348 // first check for short strings in which case we do it naively.
349 if (view.size() - location < 16) { // slow path
350 for (size_t i = location; i < view.size(); i++) {
351 if (view[i] == ':' || view[i] == '/' || view[i] == '?' ||
352 view[i] == '[') {
353 return i;
354 }
355 }
356 return size_t(view.size());
357 }
358 auto to_bitmask = [](uint8x16_t input) -> uint16_t {
360 ada_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01,
361 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
364 tmp = vpaddq_u8(tmp, tmp);
365 tmp = vpaddq_u8(tmp, tmp);
367 };
368
369 // fast path for long strings (expected to be common)
370 size_t i = location;
372 ada_make_uint8x16_t(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
373 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03);
375 ada_make_uint8x16_t(0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
378 uint8x16_t zero{0};
379 for (; i + 15 < view.size(); i += 16) {
380 uint8x16_t word = vld1q_u8((const uint8_t*)view.data() + i);
384 if (vmaxvq_u8(classify) != 0) {
387 return i + trailing_zeroes(is_non_zero);
388 }
389 }
390
391 if (i < view.size()) {
393 vld1q_u8((const uint8_t*)view.data() + view.length() - 16);
397 if (vmaxvq_u8(classify) != 0) {
400 return view.length() - 16 + trailing_zeroes(is_non_zero);
401 }
402 }
403 return size_t(view.size());
404}
405#elif ADA_SSE2
406ada_really_inline size_t find_next_host_delimiter(std::string_view view,
407 size_t location) noexcept {
408 // first check for short strings in which case we do it naively.
409 if (view.size() - location < 16) { // slow path
410 for (size_t i = location; i < view.size(); i++) {
411 if (view[i] == ':' || view[i] == '/' || view[i] == '?' ||
412 view[i] == '[') {
413 return i;
414 }
415 }
416 return size_t(view.size());
417 }
418 // fast path for long strings (expected to be common)
419 size_t i = location;
420 const __m128i mask1 = _mm_set1_epi8(':');
421 const __m128i mask2 = _mm_set1_epi8('/');
422 const __m128i mask4 = _mm_set1_epi8('?');
423 const __m128i mask5 = _mm_set1_epi8('[');
424
425 for (; i + 15 < view.size(); i += 16) {
426 __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
432 int mask = _mm_movemask_epi8(m);
433 if (mask != 0) {
434 return i + trailing_zeroes(mask);
435 }
436 }
437 if (i < view.size()) {
438 __m128i word =
439 _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
445 int mask = _mm_movemask_epi8(m);
446 if (mask != 0) {
447 return view.length() - 16 + trailing_zeroes(mask);
448 }
449 }
450 return size_t(view.length());
451}
452#else
453// : / [ ?
454static constexpr std::array<uint8_t, 256> host_delimiters = []() constexpr {
455 std::array<uint8_t, 256> result{};
456 for (int i : {':', '/', '?', '['}) {
457 result[i] = 1;
458 }
459 return result;
460}();
461// credit: @the-moisrex recommended a table-based approach
463 size_t location) noexcept {
464 auto const str = view.substr(location);
465 for (auto pos = str.begin(); pos != str.end(); ++pos) {
466 if (host_delimiters[(uint8_t)*pos]) {
467 return pos - str.begin() + location;
468 }
469 }
470 return size_t(view.size());
471}
472#endif
473
474ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
475 const bool is_special, std::string_view& view) noexcept {
484 const size_t view_size = view.size();
485 size_t location = 0;
486 bool found_colon = false;
506 if (is_special) {
507 // We move to the next delimiter.
509 // Unless we find '[' then we are going only going to have to call
510 // find_next_host_delimiter_special once.
511 for (; location < view_size;
513 if (view[location] == '[') {
514 location = view.find(']', location);
515 if (location == std::string_view::npos) {
516 // performance: view.find might get translated to a memchr, which
517 // has no notion of std::string_view::npos, so the code does not
518 // reflect the assembly.
520 break;
521 }
522 } else {
523 found_colon = view[location] == ':';
524 break;
525 }
526 }
527 } else {
528 // We move to the next delimiter.
530 // Unless we find '[' then we are going only going to have to call
531 // find_next_host_delimiter_special once.
532 for (; location < view_size;
534 if (view[location] == '[') {
535 location = view.find(']', location);
536 if (location == std::string_view::npos) {
537 // performance: view.find might get translated to a memchr, which
538 // has no notion of std::string_view::npos, so the code does not
539 // reflect the assembly.
541 break;
542 }
543 } else {
544 found_colon = view[location] == ':';
545 break;
546 }
547 }
548 }
549 // performance: remove_suffix may translate into a single instruction.
550 view.remove_suffix(view_size - location);
551 return {location, found_colon};
552}
553
554ada_really_inline void trim_c0_whitespace(std::string_view& input) noexcept {
555 while (!input.empty() &&
556 ada::unicode::is_c0_control_or_space(input.front())) {
557 input.remove_prefix(1);
558 }
559 while (!input.empty() && ada::unicode::is_c0_control_or_space(input.back())) {
560 input.remove_suffix(1);
561 }
562}
563
564ada_really_inline void parse_prepared_path(std::string_view input,
566 std::string& path) {
567 ada_log("parse_prepared_path ", input);
568 uint8_t accumulator = checkers::path_signature(input);
569 // Let us first detect a trivial case.
570 // If it is special, we check that we have no dot, no %, no \ and no
571 // character needing percent encoding. Otherwise, we check that we have no %,
572 // no dot, and no character needing percent encoding.
573 constexpr uint8_t need_encoding = 1;
574 constexpr uint8_t backslash_char = 2;
575 constexpr uint8_t dot_char = 4;
576 constexpr uint8_t percent_char = 8;
580 bool trivial_path =
581 (special ? (accumulator == 0)
583 0)) &&
586 // '4' means that we have at least one dot, but nothing that requires
587 // percent encoding or decoding. The only part that is not trivial is
588 // that we may have single dots and double dots path segments.
589 // If we have such segments, then we either have a path that begins
590 // with '.' (easy to check), or we have the sequence './'.
591 // Note: input cannot be empty, it must at least contain one character ('.')
592 // Note: we know that '\' is not present.
593 if (input[0] != '.') {
594 size_t slashdot = input.find("/.");
595 if (slashdot == std::string_view::npos) { // common case
596 trivial_path = true;
597 } else { // uncommon
598 // only three cases matter: /./, /.. or a final /
600 !(slashdot + 2 == input.size() || input[slashdot + 2] == '.' ||
601 input[slashdot + 2] == '/');
602 }
603 }
604 }
605 if (trivial_path) {
606 ada_log("parse_path trivial");
607 path += '/';
608 path += input;
609 return;
610 }
611 // We are going to need to look a bit at the path, but let us see if we can
612 // ignore percent encoding *and* backslashes *and* percent characters.
613 // Except for the trivial case, this is likely to capture 99% of paths out
614 // there.
615 bool fast_path =
616 (special &&
618 (type != ada::scheme::type::FILE);
619 if (fast_path) {
620 ada_log("parse_prepared_path fast");
621 // Here we don't need to worry about \ or percent encoding.
622 // We also do not have a file protocol. We might have dots, however,
623 // but dots must as appear as '.', and they cannot be encoded because
624 // the symbol '%' is not present.
625 size_t previous_location = 0; // We start at 0.
626 do {
627 size_t new_location = input.find('/', previous_location);
628 // std::string_view path_view = input;
629 // We process the last segment separately:
630 if (new_location == std::string_view::npos) {
631 std::string_view path_view = input.substr(previous_location);
632 if (path_view == "..") { // The path ends with ..
633 // e.g., if you receive ".." with an empty path, you go to "/".
634 if (path.empty()) {
635 path = '/';
636 return;
637 }
638 // Fast case where we have nothing to do:
639 if (path.back() == '/') {
640 return;
641 }
642 // If you have the path "/joe/myfriend",
643 // then you delete 'myfriend'.
644 path.resize(path.rfind('/') + 1);
645 return;
646 }
647 path += '/';
648 if (path_view != ".") {
649 path.append(path_view);
650 }
651 return;
652 } else {
653 // This is a non-final segment.
654 std::string_view path_view =
657 if (path_view == "..") {
658 size_t last_delimiter = path.rfind('/');
659 if (last_delimiter != std::string::npos) {
660 path.erase(last_delimiter);
661 }
662 } else if (path_view != ".") {
663 path += '/';
664 path.append(path_view);
665 }
666 }
667 } while (true);
668 } else {
669 ada_log("parse_path slow");
670 // we have reached the general case
672 std::string path_buffer_tmp;
673 do {
674 size_t location = (special && (accumulator & 2))
675 ? input.find_first_of("/\\")
676 : input.find('/');
677 std::string_view path_view = input;
678 if (location != std::string_view::npos) {
679 path_view.remove_suffix(path_view.size() - location);
680 input.remove_prefix(location + 1);
681 }
682 // path_buffer is either path_view or it might point at a percent encoded
683 // temporary file.
684 std::string_view path_buffer =
686 ada::unicode::percent_encode<false>(
689 : path_view;
690 if (unicode::is_double_dot_path_segment(path_buffer)) {
691 if ((helpers::shorten_path(path, type) || special) &&
692 location == std::string_view::npos) {
693 path += '/';
694 }
695 } else if (unicode::is_single_dot_path_segment(path_buffer) &&
696 (location == std::string_view::npos)) {
697 path += '/';
698 }
699 // Otherwise, if path_buffer is not a single-dot path segment, then:
700 else if (!unicode::is_single_dot_path_segment(path_buffer)) {
701 // If url's scheme is "file", url's path is empty, and path_buffer is a
702 // Windows drive letter, then replace the second code point in
703 // path_buffer with U+003A (:).
704 if (type == ada::scheme::type::FILE && path.empty() &&
706 path += '/';
707 path += path_buffer[0];
708 path += ':';
709 path_buffer.remove_prefix(2);
710 path.append(path_buffer);
711 } else {
712 // Append path_buffer to url's path.
713 path += '/';
714 path.append(path_buffer);
715 }
716 }
717 if (location == std::string_view::npos) {
718 return;
719 }
720 } while (true);
721 }
722}
723
724bool overlaps(std::string_view input1, const std::string& input2) noexcept {
725 ada_log("helpers::overlaps check if string_view '", input1, "' [",
726 input1.size(), " bytes] is part of string '", input2, "' [",
727 input2.size(), " bytes]");
728 return !input1.empty() && !input2.empty() && input1.data() >= input2.data() &&
729 input1.data() < input2.data() + input2.size();
730}
731
732template <class url_type>
733ada_really_inline void strip_trailing_spaces_from_opaque_path(
734 url_type& url) noexcept {
735 ada_log("helpers::strip_trailing_spaces_from_opaque_path");
736 if (!url.has_opaque_path) return;
737 if (url.has_hash()) return;
738 if (url.has_search()) return;
739
740 auto path = std::string(url.get_pathname());
741 while (!path.empty() && path.back() == ' ') {
742 path.resize(path.size() - 1);
743 }
744 url.update_base_pathname(path);
745}
746
747// @ / \\ ?
748static constexpr std::array<uint8_t, 256> authority_delimiter_special =
749 []() constexpr {
750 std::array<uint8_t, 256> result{};
751 for (int i : {'@', '/', '\\', '?'}) {
752 result[i] = 1;
753 }
754 return result;
755 }();
756// credit: @the-moisrex recommended a table-based approach
758find_authority_delimiter_special(std::string_view view) noexcept {
759 // performance note: we might be able to gain further performance
760 // with SIMD instrinsics.
761 for (auto pos = view.begin(); pos != view.end(); ++pos) {
763 return pos - view.begin();
764 }
765 }
766 return size_t(view.size());
767}
768
769// @ / ?
770static constexpr std::array<uint8_t, 256> authority_delimiter = []() constexpr {
771 std::array<uint8_t, 256> result{};
772 for (int i : {'@', '/', '?'}) {
773 result[i] = 1;
774 }
775 return result;
776}();
777// credit: @the-moisrex recommended a table-based approach
779find_authority_delimiter(std::string_view view) noexcept {
780 // performance note: we might be able to gain further performance
781 // with SIMD instrinsics.
782 for (auto pos = view.begin(); pos != view.end(); ++pos) {
783 if (authority_delimiter[(uint8_t)*pos]) {
784 return pos - view.begin();
785 }
786 }
787 return size_t(view.size());
788}
789
790} // namespace ada::helpers
791
792namespace ada {
796#undef ada_make_uint8x16_t
797} // namespace ada
Includes all definitions for Ada.
Definitions for URL specific checkers used within Ada.
Declarations for URL specific checkers used within Ada.
Common definitions for cross-platform compiler support.
#define ADA_ASSERT_TRUE(COND)
#define ada_unused
Definition common_defs.h:87
#define ada_warn_unused
Definition common_defs.h:88
#define ada_really_inline
Definition common_defs.h:84
constexpr uint8_t PATH_PERCENT_ENCODE[32]
constexpr bool is_normalized_windows_drive_letter(std::string_view input) noexcept
constexpr bool is_windows_drive_letter(std::string_view input) noexcept
Includes the definitions for helper functions.
ada_really_inline size_t find_next_host_delimiter(std::string_view view, size_t location) noexcept
Definition helpers.cpp:462
static constexpr std::array< uint8_t, 256 > authority_delimiter_special
Definition helpers.cpp:748
static constexpr std::array< uint8_t, 256 > host_delimiters
Definition helpers.cpp:454
ada_really_inline size_t find_next_host_delimiter_special(std::string_view view, size_t location) noexcept
Definition helpers.cpp:330
ada_unused std::string get_state(ada::state s)
Definition helpers.cpp:40
static constexpr std::array< uint8_t, 256 > authority_delimiter
Definition helpers.cpp:770
static constexpr std::array< uint8_t, 256 > special_host_delimiters
Definition helpers.cpp:321
ada_really_inline int trailing_zeroes(uint32_t input_num) noexcept
Definition helpers.cpp:179
@ NOT_SPECIAL
Definition scheme.h:32
Definition ada_idna.h:13
ada_warn_unused std::string to_string(encoding_type type)
state
Definition state.h:17
@ SPECIAL_RELATIVE_OR_AUTHORITY
@ SPECIAL_AUTHORITY_SLASHES
@ SPECIAL_AUTHORITY_IGNORE_SLASHES
tl::expected< result_type, ada::errors > result
ada_warn_unused ada::result< result_type > parse(std::string_view input, const result_type *base_url=nullptr)
Declarations for the URL scheme.
Definitions for all unicode specific functions.