13template <
typename out_iter>
14void encode_json(std::string_view view, out_iter out) {
16 const char* hexvalues =
17 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
18 for (uint8_t c : view) {
22 }
else if (c ==
'"') {
25 }
else if (c <= 0x1f) {
30 *out++ = hexvalues[2 * c];
31 *out++ = hexvalues[2 * c + 1];
43 return "Scheme Start";
53 return "Relative Scheme";
55 return "Relative Slash";
63 return "Path or Authority";
65 return "Special Authority Ignore Slashes";
67 return "Special Authority Slashes";
69 return "Special Relative or Authority";
81 return "unknown state";
86 std::string_view& input)
noexcept {
89 size_t location_of_first = input.find(
'#');
90 if (location_of_first == std::string_view::npos) {
93 std::string_view hash = input;
94 hash.remove_prefix(location_of_first + 1);
95 input.remove_suffix(input.size() - location_of_first);
101 size_t first_delimiter = path.find_first_of(
'/', 1);
107 first_delimiter == std::string_view::npos && !path.empty()) {
109 helpers::substring(path, 1))) {
115 size_t last_delimiter = path.rfind(
'/');
116 if (last_delimiter != std::string::npos) {
117 path.erase(last_delimiter);
126 size_t first_delimiter = path.find_first_of(
'/', 1);
132 first_delimiter == std::string_view::npos && !path.empty()) {
134 helpers::substring(path, 1))) {
141 size_t slash_loc = path.rfind(
'/');
142 if (slash_loc != std::string_view::npos) {
143 path.remove_suffix(path.size() - slash_loc);
152 std::string& input)
noexcept {
155 input.erase(std::remove_if(input.begin(), input.end(),
157 return ada::unicode::is_ascii_tab_or_newline(c);
163 size_t pos)
noexcept {
167 return input.substr(pos);
172 input.remove_suffix(input.size() - pos);
178#ifdef ADA_REGULAR_VISUAL_STUDIO
182 _BitScanForward(&ret, input_num);
185 return __builtin_ctzl(input_num);
196#ifndef ada_make_uint8x16_t
197#define ada_make_uint8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
198 x13, x14, x15, x16) \
200 static uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
201 x9, x10, x11, x12, x13, x14, x15, x16}; \
202 return vld1q_u8(array); \
207 std::string_view view,
size_t location)
noexcept {
209 if (view.size() - location < 16) {
210 for (
size_t i = location; i < view.size(); i++) {
211 if (view[i] ==
':' || view[i] ==
'/' || view[i] ==
'\\' ||
212 view[i] ==
'?' || view[i] ==
'[') {
216 return size_t(view.size());
218 auto to_bitmask = [](uint8x16_t input) -> uint16_t {
219 uint8x16_t bit_mask =
220 ada_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01,
221 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
222 uint8x16_t minput = vandq_u8(input, bit_mask);
223 uint8x16_t tmp = vpaddq_u8(minput, minput);
224 tmp = vpaddq_u8(tmp, tmp);
225 tmp = vpaddq_u8(tmp, tmp);
226 return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
231 uint8x16_t low_mask =
232 ada_make_uint8x16_t(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x03);
234 uint8x16_t high_mask =
235 ada_make_uint8x16_t(0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
237 uint8x16_t fmask = vmovq_n_u8(0xf);
239 for (; i + 15 < view.size(); i += 16) {
240 uint8x16_t word = vld1q_u8((
const uint8_t*)view.data() + i);
241 uint8x16_t lowpart = vqtbl1q_u8(low_mask, vandq_u8(word, fmask));
242 uint8x16_t highpart = vqtbl1q_u8(high_mask, vshrq_n_u8(word, 4));
243 uint8x16_t classify = vandq_u8(lowpart, highpart);
244 if (vmaxvq_u32(vreinterpretq_u32_u8(classify)) != 0) {
245 uint8x16_t is_zero = vceqq_u8(classify, zero);
246 uint16_t is_non_zero = ~to_bitmask(is_zero);
251 if (i < view.size()) {
253 vld1q_u8((
const uint8_t*)view.data() + view.length() - 16);
254 uint8x16_t lowpart = vqtbl1q_u8(low_mask, vandq_u8(word, fmask));
255 uint8x16_t highpart = vqtbl1q_u8(high_mask, vshrq_n_u8(word, 4));
256 uint8x16_t classify = vandq_u8(lowpart, highpart);
257 if (vmaxvq_u32(vreinterpretq_u32_u8(classify)) != 0) {
258 uint8x16_t is_zero = vceqq_u8(classify, zero);
259 uint16_t is_non_zero = ~to_bitmask(is_zero);
263 return size_t(view.size());
267 std::string_view view,
size_t location)
noexcept {
269 if (view.size() - location < 16) {
270 for (
size_t i = location; i < view.size(); i++) {
271 if (view[i] ==
':' || view[i] ==
'/' || view[i] ==
'\\' ||
272 view[i] ==
'?' || view[i] ==
'[') {
276 return size_t(view.size());
280 const __m128i mask1 = _mm_set1_epi8(
':');
281 const __m128i mask2 = _mm_set1_epi8(
'/');
282 const __m128i mask3 = _mm_set1_epi8(
'\\');
283 const __m128i mask4 = _mm_set1_epi8(
'?');
284 const __m128i mask5 = _mm_set1_epi8(
'[');
286 for (; i + 15 < view.size(); i += 16) {
287 __m128i word = _mm_loadu_si128((
const __m128i*)(view.data() + i));
288 __m128i m1 = _mm_cmpeq_epi8(word, mask1);
289 __m128i m2 = _mm_cmpeq_epi8(word, mask2);
290 __m128i m3 = _mm_cmpeq_epi8(word, mask3);
291 __m128i m4 = _mm_cmpeq_epi8(word, mask4);
292 __m128i m5 = _mm_cmpeq_epi8(word, mask5);
293 __m128i m = _mm_or_si128(
294 _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
295 int mask = _mm_movemask_epi8(m);
300 if (i < view.size()) {
302 _mm_loadu_si128((
const __m128i*)(view.data() + view.length() - 16));
303 __m128i m1 = _mm_cmpeq_epi8(word, mask1);
304 __m128i m2 = _mm_cmpeq_epi8(word, mask2);
305 __m128i m3 = _mm_cmpeq_epi8(word, mask3);
306 __m128i m4 = _mm_cmpeq_epi8(word, mask4);
307 __m128i m5 = _mm_cmpeq_epi8(word, mask5);
308 __m128i m = _mm_or_si128(
309 _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
310 int mask = _mm_movemask_epi8(m);
315 return size_t(view.length());
321 std::array<uint8_t, 256>
result{};
322 for (
int i : {
':',
'/',
'[',
'\\',
'?'}) {
329 std::string_view view,
size_t location)
noexcept {
330 auto const str = view.substr(location);
331 for (
auto pos = str.begin(); pos != str.end(); ++pos) {
333 return pos - str.begin() + location;
336 return size_t(view.size());
345 size_t location)
noexcept {
347 if (view.size() - location < 16) {
348 for (
size_t i = location; i < view.size(); i++) {
349 if (view[i] ==
':' || view[i] ==
'/' || view[i] ==
'?' ||
354 return size_t(view.size());
356 auto to_bitmask = [](uint8x16_t input) -> uint16_t {
357 uint8x16_t bit_mask =
358 ada_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01,
359 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
360 uint8x16_t minput = vandq_u8(input, bit_mask);
361 uint8x16_t tmp = vpaddq_u8(minput, minput);
362 tmp = vpaddq_u8(tmp, tmp);
363 tmp = vpaddq_u8(tmp, tmp);
364 return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
369 uint8x16_t low_mask =
370 ada_make_uint8x16_t(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
371 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03);
372 uint8x16_t high_mask =
373 ada_make_uint8x16_t(0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
375 uint8x16_t fmask = vmovq_n_u8(0xf);
377 for (; i + 15 < view.size(); i += 16) {
378 uint8x16_t word = vld1q_u8((
const uint8_t*)view.data() + i);
379 uint8x16_t lowpart = vqtbl1q_u8(low_mask, vandq_u8(word, fmask));
380 uint8x16_t highpart = vqtbl1q_u8(high_mask, vshrq_n_u8(word, 4));
381 uint8x16_t classify = vandq_u8(lowpart, highpart);
382 if (vmaxvq_u32(vreinterpretq_u32_u8(classify)) != 0) {
383 uint8x16_t is_zero = vceqq_u8(classify, zero);
384 uint16_t is_non_zero = ~to_bitmask(is_zero);
389 if (i < view.size()) {
391 vld1q_u8((
const uint8_t*)view.data() + view.length() - 16);
392 uint8x16_t lowpart = vqtbl1q_u8(low_mask, vandq_u8(word, fmask));
393 uint8x16_t highpart = vqtbl1q_u8(high_mask, vshrq_n_u8(word, 4));
394 uint8x16_t classify = vandq_u8(lowpart, highpart);
395 if (vmaxvq_u32(vreinterpretq_u32_u8(classify)) != 0) {
396 uint8x16_t is_zero = vceqq_u8(classify, zero);
397 uint16_t is_non_zero = ~to_bitmask(is_zero);
401 return size_t(view.size());
405 size_t location)
noexcept {
407 if (view.size() - location < 16) {
408 for (
size_t i = location; i < view.size(); i++) {
409 if (view[i] ==
':' || view[i] ==
'/' || view[i] ==
'?' ||
414 return size_t(view.size());
418 const __m128i mask1 = _mm_set1_epi8(
':');
419 const __m128i mask2 = _mm_set1_epi8(
'/');
420 const __m128i mask4 = _mm_set1_epi8(
'?');
421 const __m128i mask5 = _mm_set1_epi8(
'[');
423 for (; i + 15 < view.size(); i += 16) {
424 __m128i word = _mm_loadu_si128((
const __m128i*)(view.data() + i));
425 __m128i m1 = _mm_cmpeq_epi8(word, mask1);
426 __m128i m2 = _mm_cmpeq_epi8(word, mask2);
427 __m128i m4 = _mm_cmpeq_epi8(word, mask4);
428 __m128i m5 = _mm_cmpeq_epi8(word, mask5);
429 __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
430 int mask = _mm_movemask_epi8(m);
435 if (i < view.size()) {
437 _mm_loadu_si128((
const __m128i*)(view.data() + view.length() - 16));
438 __m128i m1 = _mm_cmpeq_epi8(word, mask1);
439 __m128i m2 = _mm_cmpeq_epi8(word, mask2);
440 __m128i m4 = _mm_cmpeq_epi8(word, mask4);
441 __m128i m5 = _mm_cmpeq_epi8(word, mask5);
442 __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
443 int mask = _mm_movemask_epi8(m);
448 return size_t(view.length());
453 std::array<uint8_t, 256>
result{};
454 for (
int i : {
':',
'/',
'?',
'['}) {
461 size_t location)
noexcept {
462 auto const str = view.substr(location);
463 for (
auto pos = str.begin(); pos != str.end(); ++pos) {
465 return pos - str.begin() + location;
468 return size_t(view.size());
473 const bool is_special, std::string_view& view)
noexcept {
482 const size_t view_size = view.size();
484 bool found_colon =
false;
509 for (; location < view_size;
511 if (view[location] ==
'[') {
512 location = view.find(
']', location);
513 if (location == std::string_view::npos) {
517 location = view_size;
521 found_colon = view[location] ==
':';
530 for (; location < view_size;
532 if (view[location] ==
'[') {
533 location = view.find(
']', location);
534 if (location == std::string_view::npos) {
538 location = view_size;
542 found_colon = view[location] ==
':';
548 view.remove_suffix(view_size - location);
549 return {location, found_colon};
553 while (!input.empty() &&
554 ada::unicode::is_c0_control_or_space(input.front())) {
555 input.remove_prefix(1);
557 while (!input.empty() && ada::unicode::is_c0_control_or_space(input.back())) {
558 input.remove_suffix(1);
565 ada_log(
"parse_prepared_path ", input);
566 uint8_t accumulator = checkers::path_signature(input);
571 constexpr uint8_t need_encoding = 1;
572 constexpr uint8_t backslash_char = 2;
573 constexpr uint8_t dot_char = 4;
574 constexpr uint8_t percent_char = 8;
579 (special ? (accumulator == 0)
580 : ((accumulator & (need_encoding | dot_char | percent_char)) ==
582 (!may_need_slow_file_handling);
583 if (accumulator == dot_char && !may_need_slow_file_handling) {
591 if (input[0] !=
'.') {
592 size_t slashdot = input.find(
"/.");
593 if (slashdot == std::string_view::npos) {
598 !(slashdot + 2 == input.size() || input[slashdot + 2] ==
'.' ||
599 input[slashdot + 2] ==
'/');
604 ada_log(
"parse_path trivial");
615 (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
618 ada_log(
"parse_prepared_path fast");
623 size_t previous_location = 0;
625 size_t new_location = input.find(
'/', previous_location);
628 if (new_location == std::string_view::npos) {
629 std::string_view path_view = input.substr(previous_location);
630 if (path_view ==
"..") {
637 if (path.back() ==
'/') {
642 path.resize(path.rfind(
'/') + 1);
646 if (path_view !=
".") {
647 path.append(path_view);
652 std::string_view path_view =
653 input.substr(previous_location, new_location - previous_location);
654 previous_location = new_location + 1;
655 if (path_view ==
"..") {
656 size_t last_delimiter = path.rfind(
'/');
657 if (last_delimiter != std::string::npos) {
658 path.erase(last_delimiter);
660 }
else if (path_view !=
".") {
662 path.append(path_view);
667 ada_log(
"parse_path slow");
669 bool needs_percent_encoding = (accumulator & 1);
670 std::string path_buffer_tmp;
672 size_t location = (special && (accumulator & 2))
673 ? input.find_first_of(
"/\\")
675 std::string_view path_view = input;
676 if (location != std::string_view::npos) {
677 path_view.remove_suffix(path_view.size() - location);
678 input.remove_prefix(location + 1);
682 std::string_view path_buffer =
683 (needs_percent_encoding &&
684 ada::unicode::percent_encode<false>(
688 if (unicode::is_double_dot_path_segment(path_buffer)) {
689 if ((helpers::shorten_path(path, type) || special) &&
690 location == std::string_view::npos) {
693 }
else if (unicode::is_single_dot_path_segment(path_buffer) &&
694 (location == std::string_view::npos)) {
698 else if (!unicode::is_single_dot_path_segment(path_buffer)) {
705 path += path_buffer[0];
707 path_buffer.remove_prefix(2);
708 path.append(path_buffer);
712 path.append(path_buffer);
715 if (location == std::string_view::npos) {
722bool overlaps(std::string_view input1,
const std::string& input2)
noexcept {
723 ada_log(
"helpers::overlaps check if string_view '", input1,
"' [",
724 input1.size(),
" bytes] is part of string '", input2,
"' [",
725 input2.size(),
" bytes]");
726 return !input1.empty() && !input2.empty() && input1.data() >= input2.data() &&
727 input1.data() < input2.data() + input2.size();
730template <
class url_type>
732 url_type& url)
noexcept {
733 ada_log(
"helpers::strip_trailing_spaces_from_opaque_path");
734 if (!url.has_opaque_path)
return;
735 if (url.has_hash())
return;
736 if (url.has_search())
return;
738 auto path = std::string(url.get_pathname());
739 while (!path.empty() && path.back() ==
' ') {
740 path.resize(path.size() - 1);
742 url.update_base_pathname(path);
748 std::array<uint8_t, 256>
result{};
749 for (uint8_t i : {
'@',
'/',
'\\',
'?'}) {
756find_authority_delimiter_special(std::string_view view)
noexcept {
759 for (
auto pos = view.begin(); pos != view.end(); ++pos) {
761 return pos - view.begin();
764 return size_t(view.size());
769 std::array<uint8_t, 256>
result{};
770 for (uint8_t i : {
'@',
'/',
'?'}) {
777find_authority_delimiter(std::string_view view)
noexcept {
780 for (
auto pos = view.begin(); pos != view.end(); ++pos) {
782 return pos - view.begin();
785 return size_t(view.size());
794#undef ada_make_uint8x16_t
Includes all definitions for Ada.
Definitions for URL specific checkers used within Ada.
Common definitions for cross-platform compiler support.
#define ADA_ASSERT_TRUE(COND)
#define ada_really_inline
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
static constexpr std::array< uint8_t, 256 > authority_delimiter_special
static constexpr std::array< uint8_t, 256 > host_delimiters
ada_really_inline size_t find_next_host_delimiter_special(std::string_view view, size_t location) noexcept
ada_unused std::string get_state(ada::state s)
static constexpr std::array< uint8_t, 256 > authority_delimiter
static constexpr std::array< uint8_t, 256 > special_host_delimiters
ada_really_inline int trailing_zeroes(uint32_t input_num) noexcept
ada_warn_unused std::string to_string(encoding_type type)
@ SPECIAL_RELATIVE_OR_AUTHORITY
@ SPECIAL_AUTHORITY_SLASHES
@ SPECIAL_AUTHORITY_IGNORE_SLASHES
tl::expected< result_type, ada::errors > result
Declarations for the URL scheme.