5#ifndef ADA_URL_AGGREGATOR_INL_H
6#define ADA_URL_AGGREGATOR_INL_H
24inline void url_aggregator::update_base_authority(
25 std::string_view base_buffer,
const ada::url_components &base) {
26 std::string_view input = base_buffer.substr(
28 ada_log(
"url_aggregator::update_base_authority ", input);
31 uint32_t diff = components.host_start - components.protocol_end;
33 buffer.erase(components.protocol_end,
34 components.host_start - components.protocol_end);
35 components.username_end = components.protocol_end;
37 if (input_starts_with_dash) {
38 input.remove_prefix(2);
40 buffer.insert(components.protocol_end,
"//");
41 components.username_end += 2;
44 size_t password_delimiter = input.find(
':');
48 if (password_delimiter != std::string_view::npos) {
50 std::string_view username = input.substr(0, password_delimiter);
51 std::string_view password = input.substr(password_delimiter + 1);
53 buffer.insert(components.protocol_end + diff, username);
54 diff += uint32_t(username.size());
55 buffer.insert(components.protocol_end + diff,
":");
56 components.username_end = components.protocol_end + diff;
57 buffer.insert(components.protocol_end + diff + 1, password);
58 diff += uint32_t(password.size()) + 1;
59 }
else if (!input.empty()) {
61 buffer.insert(components.protocol_end + diff, input);
62 components.username_end =
63 components.protocol_end + diff + uint32_t(input.size());
64 diff += uint32_t(input.size());
67 components.host_start += diff;
69 if (buffer.size() >
base.host_start && buffer[
base.host_start] !=
'@') {
70 buffer.insert(components.host_start,
"@");
73 components.host_end += diff;
74 components.pathname_start += diff;
76 components.search_start += diff;
79 components.hash_start += diff;
83inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
84 ada_log(
"url_aggregator::update_unencoded_base_hash ", input,
" [",
85 input.size(),
" bytes], buffer is '", buffer,
"' [", buffer.size(),
86 " bytes] components.hash_start = ", components.hash_start);
90 buffer.resize(components.hash_start);
92 components.hash_start = uint32_t(buffer.size());
94 bool encoding_required = unicode::percent_encode<true>(
98 if (!encoding_required) {
101 ada_log(
"url_aggregator::update_unencoded_base_hash final buffer is '",
102 buffer,
"' [", buffer.size(),
" bytes]");
107 uint32_t start, uint32_t end, std::string_view input) {
108 uint32_t current_length = end - start;
109 uint32_t input_size = uint32_t(input.size());
110 uint32_t new_difference = input_size - current_length;
112 if (current_length == 0) {
113 buffer.insert(start, input);
114 }
else if (input_size == current_length) {
115 buffer.replace(start, input_size, input);
116 }
else if (input_size < current_length) {
117 buffer.erase(start, current_length - input_size);
118 buffer.replace(start, input_size, input);
120 buffer.replace(start, current_length, input.substr(0, current_length));
121 buffer.insert(start + current_length, input.substr(current_length));
124 return new_difference;
127inline void url_aggregator::update_base_hostname(
const std::string_view input) {
128 ada_log(
"url_aggregator::update_base_hostname ", input,
" [", input.size(),
129 " bytes], buffer is '", buffer,
"' [", buffer.size(),
" bytes]");
134 add_authority_slashes_if_needed();
136 bool has_credentials = components.protocol_end + 2 < components.host_start;
137 uint32_t new_difference =
138 replace_and_resize(components.host_start, components.host_end, input);
141 buffer.insert(components.host_start,
"@");
144 components.host_end += new_difference;
145 components.pathname_start += new_difference;
147 components.search_start += new_difference;
150 components.hash_start += new_difference;
157 ada_log(
"url_aggregator::get_pathname_length");
158 uint32_t ending_index = uint32_t(buffer.size());
160 ending_index = components.search_start;
162 ending_index = components.hash_start;
164 return ending_index - components.pathname_start;
172inline void url_aggregator::update_base_search(std::string_view input) {
173 ada_log(
"url_aggregator::update_base_search ", input);
181 if (input[0] ==
'?') {
182 input.remove_prefix(1);
187 components.search_start = uint32_t(buffer.size());
190 buffer.resize(components.search_start + 1);
193 buffer.append(input);
196 components.search_start = components.hash_start;
198 buffer.erase(components.search_start,
199 components.hash_start - components.search_start);
200 components.hash_start = components.search_start;
203 buffer.insert(components.search_start,
"?");
204 buffer.insert(components.search_start + 1, input);
205 components.hash_start += uint32_t(input.size() + 1);
211inline void url_aggregator::update_base_search(
212 std::string_view input,
const uint8_t query_percent_encode_set[]) {
213 ada_log(
"url_aggregator::update_base_search ", input,
220 components.search_start = uint32_t(buffer.size());
223 buffer.resize(components.search_start + 1);
226 bool encoding_required =
227 unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
230 if (!encoding_required) {
231 buffer.append(input);
235 components.search_start = components.hash_start;
237 buffer.erase(components.search_start,
238 components.hash_start - components.search_start);
239 components.hash_start = components.search_start;
242 buffer.insert(components.search_start,
"?");
245 if (idx == input.size()) {
246 buffer.insert(components.search_start + 1, input);
247 components.hash_start += uint32_t(input.size() + 1);
249 buffer.insert(components.search_start + 1, input, 0, idx);
250 input.remove_prefix(idx);
253 std::string encoded =
254 ada::unicode::percent_encode(input, query_percent_encode_set);
255 buffer.insert(components.search_start + idx + 1, encoded);
256 components.hash_start +=
257 uint32_t(encoded.size() + idx + 1);
264inline void url_aggregator::update_base_pathname(
const std::string_view input) {
265 ada_log(
"url_aggregator::update_base_pathname '", input,
"' [", input.size(),
271 if (!begins_with_dashdash && has_dash_dot()) {
282 buffer.insert(components.pathname_start,
"/.");
283 components.pathname_start += 2;
286 uint32_t difference = replace_and_resize(
287 components.pathname_start,
290 components.search_start += difference;
293 components.hash_start += difference;
295 ada_log(
"url_aggregator::update_base_pathname end '", input,
"' [",
300inline void url_aggregator::append_base_pathname(
const std::string_view input) {
305#if ADA_DEVELOPMENT_CHECKS
308 path_expected.append(input);
310 uint32_t ending_index = uint32_t(buffer.size());
312 ending_index = components.search_start;
314 ending_index = components.hash_start;
316 buffer.insert(ending_index, input);
319 components.search_start += uint32_t(input.size());
322 components.hash_start += uint32_t(input.size());
324#if ADA_DEVELOPMENT_CHECKS
327 path_expected, path_after,
328 "append_base_pathname problem after inserting " + std::string(input));
333inline void url_aggregator::update_base_username(
const std::string_view input) {
339 add_authority_slashes_if_needed();
342 bool host_starts_with_at = buffer.size() > components.host_start &&
343 buffer[components.host_start] ==
'@';
344 uint32_t diff = replace_and_resize(components.protocol_end + 2,
345 components.username_end, input);
347 components.username_end += diff;
348 components.host_start += diff;
350 if (!input.empty() && !host_starts_with_at) {
351 buffer.insert(components.host_start,
"@");
353 }
else if (input.empty() && host_starts_with_at && !
has_password) {
356 buffer.erase(components.host_start, 1);
360 components.host_end += diff;
361 components.pathname_start += diff;
363 components.search_start += diff;
366 components.hash_start += diff;
371inline void url_aggregator::append_base_username(
const std::string_view input) {
372 ada_log(
"url_aggregator::append_base_username ", input);
375#if ADA_DEVELOPMENT_CHECKS
378 username_expected.append(input);
380 add_authority_slashes_if_needed();
387 uint32_t difference = uint32_t(input.size());
388 buffer.insert(components.username_end, input);
389 components.username_end += difference;
390 components.host_start += difference;
392 if (buffer[components.host_start] !=
'@' &&
393 components.host_start != components.host_end) {
394 buffer.insert(components.host_start,
"@");
398 components.host_end += difference;
399 components.pathname_start += difference;
401 components.search_start += difference;
404 components.hash_start += difference;
406#if ADA_DEVELOPMENT_CHECKS
409 username_expected, username_after,
410 "append_base_username problem after inserting " + std::string(input));
415inline void url_aggregator::clear_password() {
422 uint32_t diff = components.host_start - components.username_end;
423 buffer.erase(components.username_end, diff);
424 components.host_start -= diff;
425 components.host_end -= diff;
426 components.pathname_start -= diff;
428 components.search_start -= diff;
431 components.hash_start -= diff;
435inline void url_aggregator::update_base_password(
const std::string_view input) {
436 ada_log(
"url_aggregator::update_base_password ", input);
440 add_authority_slashes_if_needed();
448 update_base_username(
"");
455 uint32_t difference = uint32_t(input.size());
457 if (password_exists) {
458 uint32_t current_length =
459 components.host_start - components.username_end - 1;
460 buffer.erase(components.username_end + 1, current_length);
461 difference -= current_length;
463 buffer.insert(components.username_end,
":");
467 buffer.insert(components.username_end + 1, input);
468 components.host_start += difference;
473 if (buffer[components.host_start] !=
'@') {
474 buffer.insert(components.host_start,
"@");
478 components.host_end += difference;
479 components.pathname_start += difference;
481 components.search_start += difference;
484 components.hash_start += difference;
489inline void url_aggregator::append_base_password(
const std::string_view input) {
494#if ADA_DEVELOPMENT_CHECKS
496 std::string password_expected = std::string(
get_password());
497 password_expected.append(input);
499 add_authority_slashes_if_needed();
506 uint32_t difference = uint32_t(input.size());
508 buffer.insert(components.host_start, input);
511 buffer.insert(components.username_end,
":");
512 buffer.insert(components.username_end + 1, input);
514 components.host_start += difference;
519 if (buffer[components.host_start] !=
'@') {
520 buffer.insert(components.host_start,
"@");
524 components.host_end += difference;
525 components.pathname_start += difference;
527 components.search_start += difference;
530 components.hash_start += difference;
532#if ADA_DEVELOPMENT_CHECKS
535 password_expected, password_after,
536 "append_base_password problem after inserting " + std::string(input));
541inline void url_aggregator::update_base_port(uint32_t input) {
542 ada_log(
"url_aggregator::update_base_port");
550 std::string value = helpers::concat(
":", std::to_string(input));
551 uint32_t difference = uint32_t(value.size());
554 difference -= components.pathname_start - components.host_end;
555 buffer.erase(components.host_end,
556 components.pathname_start - components.host_end);
559 buffer.insert(components.host_end, value);
560 components.pathname_start += difference;
562 components.search_start += difference;
565 components.hash_start += difference;
567 components.port = input;
572 ada_log(
"url_aggregator::clear_port");
577 uint32_t length = components.pathname_start - components.host_end;
578 buffer.erase(components.host_end, length);
579 components.pathname_start -= length;
581 components.search_start -= length;
584 components.hash_start -= length;
590[[nodiscard]]
inline uint32_t url_aggregator::retrieve_base_port()
const {
591 ada_log(
"url_aggregator::retrieve_base_port");
592 return components.
port;
596 ada_log(
"url_aggregator::clear_search");
603 buffer.resize(components.search_start);
605 buffer.erase(components.search_start,
606 components.hash_start - components.search_start);
607 components.hash_start = components.search_start;
612#if ADA_DEVELOPMENT_CHECKS
614 "search should have been cleared on buffer=" + buffer +
615 " with " + components.to_string() +
"\n" +
to_diagram());
621 ada_log(
"url_aggregator::clear_hash");
626 buffer.resize(components.hash_start);
629#if ADA_DEVELOPMENT_CHECKS
631 "hash should have been cleared on buffer=" + buffer +
632 " with " + components.to_string() +
"\n" +
to_diagram());
637inline void url_aggregator::clear_pathname() {
638 ada_log(
"url_aggregator::clear_pathname");
640 uint32_t ending_index = uint32_t(buffer.size());
646 uint32_t pathname_length = ending_index - components.pathname_start;
647 buffer.erase(components.pathname_start, pathname_length);
648 uint32_t difference = pathname_length;
649 if (components.pathname_start == components.host_end + 2 &&
650 buffer[components.host_end] ==
'/' &&
651 buffer[components.host_end + 1] ==
'.') {
652 components.pathname_start -= 2;
653 buffer.erase(components.host_end, 2);
657 components.search_start -= difference;
660 components.hash_start -= difference;
662 ada_log(
"url_aggregator::clear_pathname completed, running checks...");
663#if ADA_DEVELOPMENT_CHECKS
665 "pathname should have been cleared on buffer=" + buffer +
666 " with " + components.to_string() +
"\n" +
to_diagram());
669 ada_log(
"url_aggregator::clear_pathname completed, running checks... ok");
672inline void url_aggregator::clear_hostname() {
673 ada_log(
"url_aggregator::clear_hostname");
675 if (!has_authority()) {
680 uint32_t hostname_length = components.host_end - components.host_start;
681 uint32_t start = components.host_start;
684 if (hostname_length > 0 && buffer[start] ==
'@') {
688 buffer.erase(start, hostname_length);
689 components.host_end = start;
690 components.pathname_start -= hostname_length;
692 components.search_start -= hostname_length;
695 components.hash_start -= hostname_length;
697#if ADA_DEVELOPMENT_CHECKS
699 "hostname should have been cleared on buffer=" + buffer +
700 " with " + components.to_string() +
"\n" +
to_diagram());
704 "hostname should have been cleared on buffer=" + buffer +
705 " with " + components.to_string() +
"\n" +
to_diagram());
710 ada_log(
"url_aggregator::has_hash");
715 ada_log(
"url_aggregator::has_search");
720 ada_log(
"url_aggregator::has_credentials");
724inline bool url_aggregator::cannot_have_credentials_or_port()
const {
725 ada_log(
"url_aggregator::cannot_have_credentials_or_port");
726 return type == ada::scheme::type::FILE ||
735[[nodiscard]]
inline bool ada::url_aggregator::has_authority() const noexcept {
736 ada_log(
"url_aggregator::has_authority");
739 return components.protocol_end + 2 <= components.host_start &&
740 helpers::substring(buffer, components.protocol_end,
741 components.protocol_end + 2) ==
"//";
744inline void ada::url_aggregator::add_authority_slashes_if_needed() noexcept {
745 ada_log(
"url_aggregator::add_authority_slashes_if_needed");
750 if (has_authority()) {
756 buffer.insert(components.protocol_end,
"//");
757 components.username_end += 2;
758 components.host_start += 2;
759 components.host_end += 2;
760 components.pathname_start += 2;
762 components.search_start += 2;
765 components.hash_start += 2;
770inline void ada::url_aggregator::reserve(uint32_t capacity) {
771 buffer.reserve(capacity);
775 ada_log(
"url_aggregator::has_non_empty_username");
776 return components.protocol_end + 2 < components.username_end;
780 ada_log(
"url_aggregator::has_non_empty_password");
781 return components.host_start - components.username_end > 0;
785 ada_log(
"url_aggregator::has_password");
787 return components.host_start > components.username_end &&
788 buffer[components.username_end] ==
':';
795 if (components.host_start == components.host_end) {
798 if (components.host_end > components.host_start + 1) {
801 return components.username_end != components.host_start;
805 return has_authority();
809 ada_log(
"url_aggregator::has_port");
812 return has_hostname() && components.pathname_start != components.host_end;
815[[nodiscard]]
inline bool url_aggregator::has_dash_dot() const noexcept {
819 ada_log(
"url_aggregator::has_dash_dot");
820#if ADA_DEVELOPMENT_CHECKS
826 buffer[components.
host_end + 1] ==
'.') ||
827 (buffer[components.
host_end] ==
':' &&
831 buffer[components.
host_end] ==
'/' &&
832 buffer[components.
host_end + 1] ==
'.') {
842 return components.pathname_start == components.host_end + 2 &&
844 buffer[components.host_end + 1] ==
'.';
849 ada_log(
"url_aggregator::get_href");
854 std::string_view view,
bool check_trailing_content)
noexcept {
855 ada_log(
"url_aggregator::parse_port('", view,
"') ", view.size());
856 if (!view.empty() && view[0] ==
'-') {
857 ada_log(
"parse_port: view[0] == '0' && view.size() > 1");
861 uint16_t parsed_port{};
862 auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
863 if (r.ec == std::errc::result_out_of_range) {
864 ada_log(
"parse_port: r.ec == std::errc::result_out_of_range");
868 ada_log(
"parse_port: ", parsed_port);
869 const size_t consumed = size_t(r.ptr - view.data());
870 ada_log(
"parse_port: consumed ", consumed);
871 if (check_trailing_content) {
873 (consumed == view.size() || view[consumed] ==
'/' ||
874 view[consumed] ==
'?' || (is_special() && view[consumed] ==
'\\'));
876 ada_log(
"parse_port: is_valid = ", is_valid);
878 ada_log(
"parse_port", r.ec == std::errc());
880 auto default_port = scheme_default_port();
881 bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
882 (default_port != parsed_port);
883 if (r.ec == std::errc() && is_port_valid) {
884 update_base_port(parsed_port);
892inline void url_aggregator::set_protocol_as_file() {
893 ada_log(
"url_aggregator::set_protocol_as_file ");
895 type = ada::scheme::type::FILE;
898 uint32_t new_difference = 5 - components.protocol_end;
900 if (buffer.empty()) {
901 buffer.append(
"file:");
903 buffer.erase(0, components.protocol_end);
904 buffer.insert(0,
"file:");
906 components.protocol_end = 5;
909 components.username_end += new_difference;
910 components.host_start += new_difference;
911 components.host_end += new_difference;
912 components.pathname_start += new_difference;
914 components.search_start += new_difference;
917 components.hash_start += new_difference;
Definitions of the character sets used by unicode functions.
Declaration of the character sets used by unicode functions.
Definitions for URL specific checkers used within Ada.
#define ADA_ASSERT_TRUE(COND)
#define ada_lifetime_bound
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)
#define ada_really_inline
Definitions for helper functions used within Ada.
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32]
constexpr bool is_digit(char x) noexcept
ada_really_inline bool begins_with(std::string_view view, std::string_view prefix)
ada_really_inline size_t percent_encode_index(const std::string_view input, const uint8_t character_set[])
std::ostream & operator<<(std::ostream &out, const ada::url &u)
Declarations for the URL scheme.
bool has_non_empty_username() const noexcept
ada_really_inline const ada::url_components & get_components() const noexcept
void clear_search() override
bool has_hostname() const noexcept
std::string_view get_hostname() const noexcept ada_lifetime_bound
bool has_non_empty_password() const noexcept
ada_really_inline bool has_credentials() const noexcept
std::string to_string() const override
std::string_view get_pathname() const noexcept ada_lifetime_bound
std::string_view get_hash() const noexcept ada_lifetime_bound
bool has_empty_hostname() const noexcept
bool has_search() const noexcept override
std::string to_diagram() const
bool validate() const noexcept
bool has_password() const noexcept
std::string_view get_search() const noexcept ada_lifetime_bound
ada_really_inline uint32_t get_pathname_length() const noexcept
bool has_hash() const noexcept override
std::string_view get_password() const noexcept ada_lifetime_bound
bool has_port() const noexcept
std::string_view get_href() const noexcept ada_lifetime_bound
std::string_view get_username() const noexcept ada_lifetime_bound
URL Component representations using offsets.
static constexpr uint32_t omitted
Definitions for unicode operations.
Definitions for all unicode specific functions.
Declaration for the basic URL definitions.
Declaration for the URL Components.