60struct alignas(32) scheme_info {
61 std::string_view scheme;
63 unsigned is_special : 1;
69UPA_API
const scheme_info* get_scheme_info(std::string_view src);
72inline constexpr const char* kURLParseError =
"URL parse error";
73inline constexpr const char* kBaseURLParseError =
"Base URL parse error";
118 url(
url&& other)
noexcept;
149 template <
class T, enable_if_str_arg_t<T> = 0>
150 inline explicit url(
const T& str_url,
const url* pbase =
nullptr)
151 :
url{ str_url, pbase, detail::kURLParseError }
160 template <
class T, enable_if_str_arg_t<T> = 0>
161 inline explicit url(
const T& str_url,
const url& base)
162 :
url{ str_url, &base, detail::kURLParseError }
171 template <
class T,
class TB, enable_if_str_arg_t<T> = 0, enable_if_str_arg_t<TB> = 0>
172 inline explicit url(
const T& str_url,
const TB& str_base)
173 :
url{ str_url,
url{ str_base, nullptr, detail::kBaseURLParseError } }
189 void swap(
url& other)
noexcept;
198 template <
class T, enable_if_str_arg_t<T> = 0>
200 const auto inp = make_str_arg(str_url);
201 return do_parse(inp.begin(), inp.end(), base);
209 template <
class T, enable_if_str_arg_t<T> = 0>
211 return parse(str_url, &base);
219 template <
class T,
class TB, enable_if_str_arg_t<T> = 0, enable_if_str_arg_t<TB> = 0>
222 const auto res = base.
parse(str_base,
nullptr);
224 ?
parse(str_url, &base)
236 template <
class T, enable_if_str_arg_t<T> = 0>
237 [[nodiscard]]
static inline bool can_parse(
const T& str_url,
const url* pbase =
nullptr) {
250 template <
class T, enable_if_str_arg_t<T> = 0>
251 [[nodiscard]]
static inline bool can_parse(
const T& str_url,
const url& base) {
264 template <
class T,
class TB, enable_if_str_arg_t<T> = 0, enable_if_str_arg_t<TB> = 0>
265 [[nodiscard]]
static inline bool can_parse(
const T& str_url,
const TB& str_base) {
281 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
282 bool href(
const StrT& str);
284 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
294 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
297 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
307 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
310 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
320 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
323 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
333 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
334 bool host(
const StrT& str);
336 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
346 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
349 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
359 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
360 bool port(
const StrT& str);
362 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
372 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
375 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
385 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
386 bool search(
const StrT& str);
388 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
398 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
399 bool hash(
const StrT& str);
401 template <
class StrT, enable_if_str_arg_t<StrT> = 0>
411 [[nodiscard]] std::string_view
href() const UPA_LIFETIMEBOUND;
413 [[nodiscard]] inline
std::string_view
get_href() const UPA_LIFETIMEBOUND {
return href(); }
423 [[nodiscard]] std::string
origin()
const;
430 [[nodiscard]] std::string_view
protocol() const UPA_LIFETIMEBOUND;
439 [[nodiscard]] std::string_view
username() const UPA_LIFETIMEBOUND;
448 [[nodiscard]] std::string_view
password() const UPA_LIFETIMEBOUND;
457 [[nodiscard]] std::string_view
host() const UPA_LIFETIMEBOUND;
459 [[nodiscard]] inline
std::string_view
get_host() const UPA_LIFETIMEBOUND {
return host(); }
466 [[nodiscard]] std::string_view
hostname() const UPA_LIFETIMEBOUND;
480 [[nodiscard]]
std::string_view
port() const UPA_LIFETIMEBOUND;
482 [[nodiscard]] inline
std::string_view
get_port() const UPA_LIFETIMEBOUND {
return port(); }
496 [[nodiscard]] std::string_view
path() const UPA_LIFETIMEBOUND;
498 [[nodiscard]] inline
std::string_view
get_path() const UPA_LIFETIMEBOUND {
return path(); }
505 [[nodiscard]] std::string_view
pathname() const UPA_LIFETIMEBOUND;
514 [[nodiscard]] std::string_view
search() const UPA_LIFETIMEBOUND;
523 [[nodiscard]] std::string_view
hash() const UPA_LIFETIMEBOUND;
525 [[nodiscard]] inline
std::string_view
get_hash() const UPA_LIFETIMEBOUND {
return hash(); }
550 [[nodiscard]] std::string_view
serialize(
bool exclude_fragment =
false) const UPA_LIFETIMEBOUND;
557 [[nodiscard]]
bool empty() const noexcept;
564 [[nodiscard]]
bool is_valid() const noexcept;
596 bool with_sep = false) const;
656 enum UrlFlag :
unsigned {
658 SCHEME_FLAG = (1u <<
SCHEME),
661 HOST_FLAG = (1u <<
HOST),
662 PORT_FLAG = (1u <<
PORT),
663 PATH_FLAG = (1u <<
PATH),
664 QUERY_FLAG = (1u <<
QUERY),
671 HOST_TYPE_MASK = (7u << HOST_TYPE_SHIFT),
675 INITIAL_FLAGS = SCHEME_FLAG | USERNAME_FLAG | PASSWORD_FLAG | PATH_FLAG,
679 template <
class T, enable_if_str_arg_t<T> = 0>
680 explicit url(
const T& str_url,
const url* base,
const char* what_arg);
683 template <
typename CharT>
686 template <
class T, enable_if_str_arg_t<T> = 0>
690 void set_scheme_str(std::string_view str);
691 void set_scheme(
const url& src);
692 void set_scheme(std::string_view str);
693 void set_scheme(std::size_t scheme_length);
696 std::string origin_of_special_url()
const;
699 std::string_view get_path_first_string(std::size_t len)
const UPA_LIFETIMEBOUND;
701 bool get_path_rem_last(std::size_t& path_end, std::size_t& path_segment_count)
const;
702 bool get_shorten_path(std::size_t& path_end, std::size_t& path_segment_count)
const;
705 void set_flag(UrlFlag flag)
noexcept;
707 void set_has_opaque_path() noexcept;
709 void set_host_type(
HostType ht) noexcept;
712 bool canHaveUsernamePasswordPort() const;
715 void move_record(
url& other) noexcept;
718 void clear_search_params() noexcept;
719 void parse_search_params();
722 std::
string norm_url_;
724 const detail::scheme_info* scheme_inf_ =
nullptr;
725 unsigned flags_ = INITIAL_FLAGS;
726 std::size_t path_segment_count_ = 0;
727 detail::url_search_params_ptr search_params_ptr_;
731 friend struct std::hash<
url>;
732 friend class detail::url_serializer;
733 friend class detail::url_setter;
734 friend class detail::url_parser;
742class UPA_SO_VISIBLE url_serializer :
public host_output {
744 url_serializer() =
delete;
745 url_serializer(
const url_serializer&) =
delete;
746 url_serializer& operator=(
const url_serializer&) =
delete;
748 inline explicit url_serializer(
url& dest_url,
bool need_save =
true)
749 : host_output(need_save)
751 , last_pt_(
url::SCHEME)
754 ~url_serializer()
override =
default;
756 inline void new_url() {
760 inline virtual void reserve(std::size_t new_cap) {
761 util::reserve(url_.norm_url_, new_cap);
765 inline void set_scheme(
const url& src) { url_.set_scheme(src); }
766 inline void set_scheme(std::string_view str) { url_.set_scheme(str); }
767 inline void set_scheme(std::size_t scheme_length) { url_.set_scheme(scheme_length); }
770 virtual std::string& start_scheme();
771 virtual void save_scheme();
774 void fill_parts_offset(url::PartType t1, url::PartType t2, std::size_t offset);
775 virtual std::string& start_part(url::PartType new_pt);
776 virtual void save_part();
778 inline virtual void clear_part(url::PartType ) {}
781 void set_empty_host();
784 virtual void empty_host();
787 std::string& hostStart()
override;
788 void hostDone(HostType ht)
override;
793 void append_empty_path_segment();
795 virtual std::string& start_path_segment();
796 virtual void save_path_segment();
797 virtual void commit_path();
799 std::string& start_path_string();
800 void save_path_string();
802 virtual void shorten_path();
806 using PathOpFn = bool (url::*)(std::size_t& path_end, std::size_t& segment_count)
const;
807 void append_parts(
const url& src, url::PartType t1, url::PartType t2, PathOpFn pathOpFn =
nullptr);
810 inline void set_flag(
const url::UrlFlag flag) { url_.set_flag(flag); }
811 inline void set_host_type(
const HostType ht) { url_.set_host_type(ht); }
814 inline void set_has_opaque_path() {
815 assert(last_pt_ == url::SCHEME);
816 url_.set_has_opaque_path();
820 inline std::string_view get_part_view(url::PartType t)
const {
return url_.get_part_view(t); }
821 inline bool is_empty(
const url::PartType t)
const {
return url_.is_empty(t); }
822 inline virtual bool is_empty_path()
const {
823 assert(!url_.has_opaque_path());
825 return url_.path_segment_count_ == 0;
827 inline bool is_null(
const url::PartType t)
const noexcept {
return url_.is_null(t); }
828 inline bool is_special_scheme() const noexcept {
return url_.is_special_scheme(); }
829 inline bool is_file_scheme() const noexcept {
return url_.is_file_scheme(); }
830 inline bool has_credentials()
const {
return url_.has_credentials(); }
831 inline const detail::scheme_info* scheme_inf() const noexcept {
return url_.scheme_inf_; }
832 inline int port_int()
const {
return url_.port_int(); }
835 void adjust_path_prefix();
837 std::size_t get_part_pos(url::PartType pt)
const;
838 std::size_t get_part_len(url::PartType pt)
const;
839 void replace_part(url::PartType new_pt,
const char* str, std::size_t len);
840 void replace_part(url::PartType last_pt,
const char* str, std::size_t len,
841 url::PartType first_pt, std::size_t len0);
846 url::PartType last_pt_;
850class UPA_SO_VISIBLE url_setter :
public url_serializer {
852 url_setter() =
delete;
853 url_setter(
const url_setter&) =
delete;
854 url_setter& operator=(
const url_setter&) =
delete;
856 inline explicit url_setter(url& dest_url)
857 : url_serializer(dest_url)
859 , curr_pt_(url::SCHEME)
862 ~url_setter()
override =
default;
865 void reserve(std::size_t new_cap)
override;
868 std::string& start_scheme()
override;
869 void save_scheme()
override;
872 std::string& start_part(url::PartType new_pt)
override;
873 void save_part()
override;
875 void clear_part(url::PartType pt)
override;
876 void empty_part(url::PartType pt);
878 void empty_host()
override;
881 std::string& start_path_segment()
override;
882 void save_path_segment()
override;
883 void commit_path()
override;
885 void shorten_path()
override;
888 bool is_empty_path()
const override;
891 url::PartType find_last_part(url::PartType pt)
const;
898 std::vector<std::size_t> path_seg_end_;
900 url::PartType curr_pt_;
911 special_relative_or_authority_state,
912 path_or_authority_state,
914 relative_slash_state,
915 special_authority_slashes_state,
916 special_authority_ignore_slashes_state,
931 template <
typename CharT>
932 static validation_errc url_parse(url_serializer& urls,
const CharT* first,
const CharT* last,
const url* base, State state_override);
934 template <
typename CharT>
935 static validation_errc parse_host(url_serializer& urls,
const CharT* first,
const CharT* last);
937 template <
typename CharT>
938 static void parse_path(url_serializer& urls,
const CharT* first,
const CharT* last);
941 template <
typename CharT>
942 static void do_path_segment(
const CharT* pointer,
const CharT* last, std::string& output);
944 template <
typename CharT>
945 static void do_opaque_path(
const CharT* pointer,
const CharT* last, std::string& output);
960constexpr int port_from_str(
const char* first,
const char* last)
noexcept {
962 for (
auto it = first; it != last; ++it) {
963 port = port * 10 + (*it -
'0');
971template <
typename CharT>
972constexpr bool is_trim_char(CharT ch)
noexcept {
973 return util::to_unsigned(ch) <=
' ';
977template <
typename CharT>
978constexpr bool is_removable_char(CharT ch)
noexcept {
979 return ch ==
'\r' || ch ==
'\n' || ch ==
'\t';
982template <
typename CharT>
983constexpr void do_trim(
const CharT*& first,
const CharT*& last)
noexcept {
985 while (first < last && is_trim_char(*first))
988 while (first < last && is_trim_char(*(last-1)))
994template <
typename CharT>
995inline void do_remove_whitespace(
const CharT*& first,
const CharT*& last, simple_buffer<CharT>& buff) {
999 for (
auto it = first; it < last; ++it) {
1000 if (!is_removable_char(*it))
1003 buff.reserve(last - first);
1004 buff.append(first, it);
1005 for (; it < last; ++it) {
1006 if (!is_removable_char(*it))
1007 buff.push_back(*it);
1009 first = buff.data();
1010 last = buff.data() + buff.size();
1017template<
class InputIt,
class T>
1018constexpr InputIt find_last(InputIt first, InputIt last,
const T& value) {
1019 for (
auto it = last; it > first;) {
1021 if (*it == value)
return it;
1028template <
typename CharT>
1029constexpr bool is_slash(CharT ch)
noexcept {
1030 return ch ==
'/' || ch ==
'\\';
1033template <
typename CharT>
1034constexpr bool is_posix_slash(CharT ch)
noexcept {
1038template <
typename CharT>
1039constexpr bool is_windows_slash(CharT ch)
noexcept {
1040 return ch ==
'\\' || ch ==
'/';
1045template <
typename CharT>
1046constexpr bool is_first_scheme_char(CharT ch)
noexcept {
1047 return is_ascii_alpha(ch);
1050template <
typename CharT>
1051constexpr bool is_authority_end_char(CharT c)
noexcept {
1052 return c ==
'/' || c ==
'?' || c ==
'#';
1055template <
typename CharT>
1056constexpr bool is_special_authority_end_char(CharT c)
noexcept {
1057 return c ==
'/' || c ==
'?' || c ==
'#' || c ==
'\\';
1063template <
typename CharT>
1064constexpr bool is_windows_drive(CharT c1, CharT c2)
noexcept {
1065 return is_ascii_alpha(c1) && (c2 ==
':' || c2 ==
'|');
1069template <
typename CharT>
1070constexpr bool is_normalized_windows_drive(CharT c1, CharT c2)
noexcept {
1071 return is_ascii_alpha(c1) && c2 ==
':';
1075template <
typename CharT>
1076constexpr bool starts_with_windows_drive(
const CharT* pointer,
const CharT* last)
noexcept {
1077 const auto length = last - pointer;
1079 (length == 2 || (length > 2 && detail::is_special_authority_end_char(pointer[2]))) &&
1080 detail::is_windows_drive(pointer[0], pointer[1]);
1095constexpr bool pathname_has_windows_os_drive(std::string_view pathname)
noexcept {
1097 (pathname.length() == 3 || (pathname.length() > 3 && is_windows_slash(pathname[3]))) &&
1098 is_windows_slash(pathname[0]) &&
1099 is_normalized_windows_drive(pathname[1], pathname[2]);
1105template <
typename CharT>
1106constexpr const CharT* is_windows_os_drive_absolute_path(
const CharT* pointer,
const CharT* last)
noexcept {
1107 return (last - pointer > 2 &&
1108 is_normalized_windows_drive(pointer[0], pointer[1]) &&
1109 is_windows_slash(pointer[2]))
1110 ? pointer + 3 :
nullptr;
1119 : norm_url_(std::move(other.norm_url_))
1120 , part_end_(other.part_end_)
1121 , scheme_inf_(other.scheme_inf_)
1122 , flags_(other.flags_)
1123 , path_segment_count_(other.path_segment_count_)
1124 , search_params_ptr_(std::move(other.search_params_ptr_))
1126 search_params_ptr_.set_url_ptr(
this);
1132 search_params_ptr_ = std::move(other.search_params_ptr_);
1135 search_params_ptr_.set_url_ptr(
this);
1141 if (search_params_ptr_) {
1142 if (other.search_params_ptr_) {
1144 search_params_ptr_->move_params(std::move(*other.search_params_ptr_));
1149 search_params_ptr_->move_params(std::move(params));
1157inline void url::move_record(
url& other)
noexcept {
1158 norm_url_ = std::move(other.norm_url_);
1159 part_end_ = other.part_end_;
1160 scheme_inf_ = other.scheme_inf_;
1161 flags_ = other.flags_;
1162 path_segment_count_ = other.path_segment_count_;
1167inline std::string_view
url::href() const UPA_LIFETIMEBOUND {
1184 return origin_of_special_url();
1192 return path_url.origin_of_special_url();
1197inline std::string url::origin_of_special_url()
const {
1199 std::string str_origin(norm_url_, 0, part_end_[
SCHEME_SEP]);
1201 str_origin.append(norm_url_.data() + part_end_[
HOST_START], norm_url_.data() + part_end_[
PORT]);
1207 return { norm_url_.data(), part_end_[
SCHEME] ? part_end_[
SCHEME] + 1 : 0 };
1218inline std::string_view
url::host() const UPA_LIFETIMEBOUND {
1224 return { norm_url_.data() + b, e - b };
1232 return static_cast<HostType>((flags_ & HOST_TYPE_MASK) >> HOST_TYPE_SHIFT);
1235inline std::string_view
url::port() const UPA_LIFETIMEBOUND {
1241 return !vport.empty() ? detail::port_from_str(vport.data(), vport.data() + vport.length()) : -1;
1247 return detail::port_from_str(vport.data(), vport.data() + vport.length());
1248 return scheme_inf_ ? scheme_inf_->default_port : -1;
1252inline std::string_view
url::path() const UPA_LIFETIMEBOUND {
1254 const std::size_t b = part_end_[
PATH - 1];
1255 const std::size_t e = part_end_[
QUERY] ? part_end_[
QUERY] : part_end_[
PATH];
1256 return { norm_url_.data() + b, e ? e - b : 0 };
1266 const std::size_t b = part_end_[
QUERY - 1];
1267 const std::size_t e = part_end_[
QUERY];
1272 return { norm_url_.data() + b, e - b };
1275inline std::string_view
url::hash() const UPA_LIFETIMEBOUND {
1276 const std::size_t b = part_end_[
FRAGMENT - 1];
1277 const std::size_t e = part_end_[
FRAGMENT];
1282 return { norm_url_.data() + b, e - b };
1286 if (!search_params_ptr_)
1287 search_params_ptr_.init(
this);
1288 return *search_params_ptr_;
1292 if (search_params_ptr_)
1293 return std::move(*search_params_ptr_);
1297inline void url::clear_search_params() noexcept {
1298 if (search_params_ptr_)
1299 search_params_ptr_.clear_params();
1302inline void url::parse_search_params() {
1303 if (search_params_ptr_)
1307inline std::string_view
url::serialize(
bool exclude_fragment)
const UPA_LIFETIMEBOUND {
1308 if (exclude_fragment && part_end_[
FRAGMENT])
1309 return { norm_url_.data(), part_end_[
QUERY] };
1316 return norm_url_.
empty();
1320 return !!(flags_ & VALID_FLAG);
1325 return { norm_url_.data(), part_end_[
SCHEME] };
1327 const std::size_t b = part_end_[t - 1] + detail::kPartStart[t];
1328 const std::size_t e = part_end_[t];
1329 return { norm_url_.data() + b, e > b ? e - b : 0 };
1334 return part_end_[
SCHEME] == 0;
1336 const std::size_t b = part_end_[t - 1] + detail::kPartStart[t];
1337 const std::size_t e = part_end_[t];
1342 return !(flags_ & (1u << t));
1346 return scheme_inf_ && scheme_inf_->is_special;
1350 return scheme_inf_ && scheme_inf_->is_file;
1354 return scheme_inf_ && scheme_inf_->is_http;
1363inline void url::set_scheme_str(std::string_view str) {
1365 part_end_[
SCHEME] = str.length();
1366 norm_url_.append(str);
1370inline void url::set_scheme(
const url& src) {
1371 set_scheme_str(src.get_part_view(
SCHEME));
1372 scheme_inf_ = src.scheme_inf_;
1375inline void url::set_scheme(std::string_view str) {
1376 set_scheme_str(str);
1377 scheme_inf_ = detail::get_scheme_info(str);
1380inline void url::set_scheme(std::size_t scheme_length) {
1381 part_end_[
SCHEME] = scheme_length;
1387inline void url::set_flag(
const UrlFlag flag)
noexcept {
1392 return !!(flags_ & OPAQUE_PATH_FLAG);
1395inline void url::set_has_opaque_path() noexcept {
1396 set_flag(OPAQUE_PATH_FLAG);
1399inline void url::set_host_type(
const HostType ht)
noexcept {
1400 flags_ = (flags_ & ~HOST_TYPE_MASK) | HOST_FLAG | (
static_cast<unsigned int>(ht) << HOST_TYPE_SHIFT);
1403inline bool url::canHaveUsernamePasswordPort()
const {
1409template <
class T, enable_if_str_arg_t<T>>
1410inline url::url(
const T& str_url,
const url* base,
const char* what_arg) {
1411 const auto inp = make_str_arg(str_url);
1412 const auto res = do_parse(inp.begin(), inp.end(), base);
1414 throw url_error(res, what_arg);
1422 scheme_inf_ =
nullptr;
1423 flags_ = INITIAL_FLAGS;
1424 path_segment_count_ = 0;
1425 clear_search_params();
1429 url tmp{ std::move(*
this) };
1430 *
this = std::move(other);
1431 other = std::move(tmp);
1440template <
typename CharT>
1441inline validation_errc url::do_parse(
const CharT* first,
const CharT* last,
const url* base) {
1443 detail::url_serializer urls(*
this);
1453 detail::do_trim(first, last);
1456 return detail::url_parser::url_parse(urls, first, last, base, detail::url_parser::not_set_state);
1459 set_flag(VALID_FLAG);
1460 parse_search_params();
1465template <
class T, enable_if_str_arg_t<T>>
1467 const auto inp = make_str_arg(str_url);
1468 const auto* first = inp.begin();
1469 const auto* last = inp.end();
1477 if (base && !base->is_valid())
1481 detail::do_trim(first, last);
1484 return detail::url_parser::url_parse(urls, first, last, base, detail::url_parser::not_set_state);
1487 set_flag(VALID_FLAG);
1493template <
class StrT, enable_if_str_arg_t<StrT>>
1497 const auto inp = make_str_arg(str);
1505template <
class StrT, enable_if_str_arg_t<StrT>>
1508 detail::url_setter urls(*
this);
1510 const auto inp = make_str_arg(str);
1511 return detail::url_parser::url_parse(urls, inp.begin(), inp.end(),
nullptr, detail::url_parser::scheme_start_state) ==
validation_errc::ok;
1516template <
class StrT, enable_if_str_arg_t<StrT>>
1518 if (canHaveUsernamePasswordPort()) {
1519 detail::url_setter urls(*
this);
1521 const auto inp = make_str_arg(str);
1532template <
class StrT, enable_if_str_arg_t<StrT>>
1534 if (canHaveUsernamePasswordPort()) {
1535 detail::url_setter urls(*
this);
1537 const auto inp = make_str_arg(str);
1548template <
class StrT, enable_if_str_arg_t<StrT>>
1551 detail::url_setter urls(*
this);
1553 const auto inp = make_str_arg(str);
1554 return detail::url_parser::url_parse(urls, inp.begin(), inp.end(),
nullptr, detail::url_parser::host_state) ==
validation_errc::ok;
1559template <
class StrT, enable_if_str_arg_t<StrT>>
1562 detail::url_setter urls(*
this);
1564 const auto inp = make_str_arg(str);
1565 return detail::url_parser::url_parse(urls, inp.begin(), inp.end(),
nullptr, detail::url_parser::hostname_state) ==
validation_errc::ok;
1570template <
class StrT, enable_if_str_arg_t<StrT>>
1572 if (canHaveUsernamePasswordPort()) {
1573 detail::url_setter urls(*
this);
1575 const auto inp = make_str_arg(str);
1576 const auto* first = inp.begin();
1577 const auto* last = inp.end();
1579 if (first == last) {
1583 return detail::url_parser::url_parse(urls, first, last,
nullptr, detail::url_parser::port_state) ==
validation_errc::ok;
1588template <
class StrT, enable_if_str_arg_t<StrT>>
1591 detail::url_setter urls(*
this);
1593 const auto inp = make_str_arg(str);
1594 return detail::url_parser::url_parse(urls, inp.begin(), inp.end(),
nullptr, detail::url_parser::path_start_state) ==
validation_errc::ok;
1599template <
class StrT, enable_if_str_arg_t<StrT>>
1604 detail::url_setter urls(*
this);
1606 const auto inp = make_str_arg(str);
1607 const auto* first = inp.begin();
1608 const auto* last = inp.end();
1610 if (first == last) {
1613 clear_search_params();
1616 if (*first ==
'?') ++first;
1617 res = detail::url_parser::url_parse(urls, first, last,
nullptr, detail::url_parser::query_state) ==
validation_errc::ok;
1620 parse_search_params();
1625template <
class StrT, enable_if_str_arg_t<StrT>>
1628 detail::url_setter urls(*
this);
1630 const auto inp = make_str_arg(str);
1631 const auto* first = inp.begin();
1632 const auto* last = inp.end();
1634 if (first == last) {
1638 if (*first ==
'#') ++first;
1639 return detail::url_parser::url_parse(urls, first, last,
nullptr, detail::url_parser::fragment_state) ==
validation_errc::ok;
1650template <
typename CharT>
1651inline validation_errc url_parser::url_parse(url_serializer& urls,
const CharT* first,
const CharT* last,
const url* base, State state_override)
1653 using UCharT = std::make_unsigned_t<CharT>;
1656 simple_buffer<CharT> buff_no_ws;
1657 detail::do_remove_whitespace(first, last, buff_no_ws);
1660 if (urls.need_save()) {
1662 const auto length = std::distance(first, last);
1663 urls.reserve(length + 32);
1666#ifdef UPA_URL_USE_ENCODING
1667 const char* encoding =
"UTF-8";
1671 auto pointer = first;
1672 State state = state_override ? state_override : scheme_start_state;
1675 if (state == scheme_start_state) {
1676 if (pointer != last && detail::is_first_scheme_char(*pointer)) {
1677 state = scheme_state;
1678 }
else if (!state_override) {
1679 state = no_scheme_state;
1686 if (state == scheme_state) {
1694 const auto end_of_scheme = std::find_if_not(pointer + 1, last, detail::is_scheme_char<CharT>);
1695 const bool is_scheme = end_of_scheme != last
1696 ? *end_of_scheme ==
':'
1697 : state_override != not_set_state;
1701 std::string& str_scheme = urls.start_scheme();
1705 for (
auto it = pointer; it != end_of_scheme; ++it)
1706 str_scheme.push_back(
static_cast<char>(*it | 0x20));
1708 if (state_override) {
1709 const auto* scheme_inf = detail::get_scheme_info(str_scheme);
1710 const bool is_special_old = urls.is_special_scheme();
1711 const bool is_special_new = scheme_inf && scheme_inf->is_special;
1712 if (is_special_old != is_special_new)
1715 if (scheme_inf && scheme_inf->is_file && (urls.has_credentials() || !urls.is_null(
url::PORT)))
1718 if (urls.is_file_scheme() && urls.is_empty(
url::HOST))
1727 if (scheme_inf && scheme_inf->default_port >= 0 &&
1728 urls.port_int() == scheme_inf->default_port) {
1738 pointer = end_of_scheme + 1;
1739 if (urls.is_file_scheme()) {
1743 if (urls.is_special_scheme()) {
1746 state = special_relative_or_authority_state;
1748 state = special_authority_slashes_state;
1750 }
else if (pointer < last && *pointer ==
'/') {
1751 state = path_or_authority_state;
1756 urls.set_has_opaque_path();
1761 state = opaque_path_state;
1764 }
else if (!state_override) {
1765 state = no_scheme_state;
1772 if (state == no_scheme_state) {
1775 if (pointer < last && *pointer ==
'#') {
1776 urls.set_scheme(*base);
1779 state = fragment_state;
1796 if (state == special_relative_or_authority_state) {
1797 if (last - pointer > 1 && pointer[0] ==
'/' && pointer[1] ==
'/') {
1798 state = special_authority_ignore_slashes_state;
1802 state = relative_state;
1806 if (state == path_or_authority_state) {
1807 if (pointer < last && pointer[0] ==
'/') {
1808 state = authority_state;
1815 if (state == relative_state) {
1817 urls.set_scheme(*base);
1818 if (pointer == last) {
1825 const CharT ch = *pointer++;
1828 state = relative_slash_state;
1834 state = query_state;
1840 state = fragment_state;
1843 if (urls.is_special_scheme()) {
1845 state = relative_slash_state;
1858 if (state == relative_slash_state) {
1860 switch (pointer != last ? *pointer : 0) {
1862 if (urls.is_special_scheme())
1863 state = special_authority_ignore_slashes_state;
1865 state = authority_state;
1869 if (urls.is_special_scheme()) {
1871 state = special_authority_ignore_slashes_state;
1884 if (state == special_authority_slashes_state) {
1885 if (last - pointer > 1 && pointer[0] ==
'/' && pointer[1] ==
'/') {
1886 state = special_authority_ignore_slashes_state;
1890 state = special_authority_ignore_slashes_state;
1894 if (state == special_authority_ignore_slashes_state) {
1896 while (it < last && detail::is_slash(*it)) ++it;
1899 state = authority_state;
1904 if (state == authority_state) {
1906 const auto end_of_authority = urls.is_special_scheme() ?
1907 std::find_if(pointer, last, detail::is_special_authority_end_char<CharT>) :
1908 std::find_if(pointer, last, detail::is_authority_end_char<CharT>);
1910 const auto it_eta = detail::find_last(pointer, end_of_authority,
static_cast<CharT
>(
'@'));
1911 if (it_eta != end_of_authority) {
1912 if (std::distance(it_eta, end_of_authority) == 1) {
1919 if (urls.need_save()) {
1920 const auto it_colon = std::find(pointer, it_eta,
':');
1922 const bool not_empty_password = std::distance(it_colon, it_eta) > 1;
1923 if (not_empty_password || std::distance(pointer, it_colon) > 0 ) {
1929 if (not_empty_password) {
1937 pointer = it_eta + 1;
1942 if (state == host_state || state == hostname_state) {
1943 if (state_override && urls.is_file_scheme()) {
1944 state = file_host_state;
1946 const auto end_of_authority = urls.is_special_scheme() ?
1947 std::find_if(pointer, last, detail::is_special_authority_end_char<CharT>) :
1948 std::find_if(pointer, last, detail::is_authority_end_char<CharT>);
1950 bool in_square_brackets =
false;
1951 bool is_port =
false;
1952 auto it_host_end = pointer;
1953 for (; it_host_end < end_of_authority; ++it_host_end) {
1954 const CharT ch = *it_host_end;
1956 if (!in_square_brackets) {
1960 }
else if (ch ==
'[') {
1961 in_square_brackets =
true;
1962 }
else if (ch ==
']') {
1963 in_square_brackets =
false;
1968 if (pointer == it_host_end) {
1970 if (is_port || urls.is_special_scheme()) {
1976 if (state_override && (urls.has_credentials() || !urls.is_null(
url::PORT))) {
1983 if (is_port && state_override == hostname_state)
1987 const auto res = parse_host(urls, pointer, it_host_end);
1993 pointer = it_host_end + 1;
1996 pointer = it_host_end;
1997 state = path_start_state;
2004 if (state == port_state) {
2005 const auto end_of_digits = std::find_if_not(pointer, last, detail::is_ascii_digit<CharT>);
2007 const bool is_end_of_authority =
2008 end_of_digits == last ||
2009 detail::is_authority_end_char(end_of_digits[0]) ||
2010 (end_of_digits[0] ==
'\\' && urls.is_special_scheme());
2012 if (is_end_of_authority || state_override) {
2013 if (pointer < end_of_digits) {
2016 pointer = std::find_if(pointer, end_of_digits - 1, [](CharT c) {
return c !=
'0'; });
2018 if (std::distance(pointer, end_of_digits) > 5)
2022 for (
auto it = pointer; it < end_of_digits; ++it)
2023 port = port * 10 + (*it -
'0');
2028 if (urls.need_save()) {
2030 if (urls.scheme_inf() ==
nullptr || urls.scheme_inf()->default_port != port) {
2031 util::append(urls.start_part(
url::PORT), str_arg<CharT>{ pointer, end_of_digits });
2033 urls.set_flag(url::PORT_FLAG);
2042 }
else if (state_override)
2044 state = path_start_state;
2045 pointer = end_of_digits;
2052 if (state == file_state) {
2053 if (!urls.is_file_scheme())
2054 urls.set_scheme(std::string_view{
"file", 4 });
2056 urls.set_empty_host();
2058 switch (pointer != last ? *pointer : 0) {
2062 state = file_slash_state;
2068 if (pointer == last) {
2078 state = query_state;
2084 state = fragment_state;
2088 if (!detail::starts_with_windows_drive(pointer, last)) {
2105 if (state == file_slash_state) {
2107 switch (pointer != last ? *pointer : 0) {
2111 state = file_host_state;
2123 if (!detail::starts_with_windows_drive(pointer, last)) {
2124 const std::string_view base_path = base->get_path_first_string(2);
2126 if (base_path.length() == 2 &&
2127 detail::is_normalized_windows_drive(base_path[0], base_path[1])) {
2129 std::string& str_path = urls.start_path_segment();
2130 str_path.append(base_path.data(), 2);
2131 urls.save_path_segment();
2140 if (state == file_host_state) {
2141 const auto end_of_authority = std::find_if(pointer, last, detail::is_special_authority_end_char<CharT>);
2143 if (pointer == end_of_authority) {
2146 urls.set_empty_host();
2150 state = path_start_state;
2151 }
else if (!state_override && end_of_authority - pointer == 2 &&
2152 detail::is_windows_drive(pointer[0], pointer[1])) {
2161 const auto res = parse_host(urls, pointer, end_of_authority);
2165 if (urls.get_part_view(
url::HOST) == std::string_view{
"localhost", 9 }) {
2172 pointer = end_of_authority;
2173 state = path_start_state;
2177 if (!urls.need_save())
2180 if (state == path_start_state) {
2181 if (urls.is_special_scheme()) {
2182 if (pointer != last) {
2190 if (pointer == last) {
2194 urls.append_empty_path_segment();
2199 }
else if (pointer != last) {
2200 if (!state_override) {
2201 switch (pointer[0]) {
2204 state = query_state;
2209 state = fragment_state;
2220 if (pointer[0] ==
'/') ++pointer;
2225 if (state_override && urls.is_null(
url::HOST))
2226 urls.append_empty_path_segment();
2233 if (state == path_state) {
2234 const auto end_of_path = state_override ? last :
2235 std::find_if(pointer, last, [](CharT c) {
return c ==
'?' || c ==
'#'; });
2237 parse_path(urls, pointer, end_of_path);
2238 pointer = end_of_path;
2243 if (pointer == last)
2246 const CharT ch = *pointer++;
2249 state = query_state;
2253 state = fragment_state;
2257 if (state == opaque_path_state) {
2258 const auto end_of_path =
2259 std::find_if(pointer, last, [](CharT c) {
return c ==
'?' || c ==
'#'; });
2263 std::string& str_path = urls.start_path_string();
2264 do_opaque_path(pointer, end_of_path, str_path);
2265 urls.save_path_string();
2266 pointer = end_of_path;
2268 if (pointer == last)
2271 const CharT ch = *pointer++;
2274 state = query_state;
2278 state = fragment_state;
2282 if (state == query_state) {
2283 const auto end_of_query = state_override ? last : std::find(pointer, last,
'#');
2292#ifdef UPA_URL_USE_ENCODING
2294 if (!urls.scheme_inf() || !urls.scheme_inf()->is_special || urls.scheme_inf()->is_ws)
2300 const auto& query_cpset = urls.is_special_scheme()
2307 std::string& str_query = urls.start_part(
url::QUERY);
2309 while (pointer != end_of_query) {
2312 const auto uch =
static_cast<UCharT
>(*pointer);
2315 detail::append_utf8_percent_encoded_char(pointer, end_of_query, str_query);
2318 const auto uc =
static_cast<unsigned char>(uch);
2319 if (!detail::is_char_in_set(uc, query_cpset))
2320 detail::append_percent_encoded_byte(uc, str_query);
2322 str_query.push_back(uc);
2331 urls.set_flag(url::QUERY_FLAG);
2333 pointer = end_of_query;
2334 if (pointer == last)
2338 state = fragment_state;
2342 if (state == fragment_state) {
2345 while (pointer < last) {
2347 const auto uch =
static_cast<UCharT
>(*pointer);
2350 detail::append_utf8_percent_encoded_char(pointer, last, str_frag);
2353 const auto uc =
static_cast<unsigned char>(uch);
2355 str_frag.push_back(uc);
2358 detail::append_percent_encoded_byte(uc, str_frag);
2367 urls.set_flag(url::FRAGMENT_FLAG);
2375template <
typename CharT>
2376inline validation_errc url_parser::parse_host(url_serializer& urls,
const CharT* first,
const CharT* last) {
2377 return host_parser::parse_host(first, last, !urls.is_special_scheme(), urls);
2380template <
typename CharT>
2381inline void url_parser::parse_path(url_serializer& urls,
const CharT* first,
const CharT* last) {
2385 static constexpr auto escaped_dot = [](
const CharT*
const pointer)
constexpr ->
bool {
2387 return pointer[0] ==
'%' && pointer[1] ==
'2' && (pointer[2] | 0x20) ==
'e';
2389 static constexpr auto double_dot = [](
const CharT*
const pointer,
const std::size_t len)
constexpr ->
bool {
2392 return pointer[0] ==
'.' && pointer[1] ==
'.';
2394 return (pointer[0] ==
'.' && escaped_dot(pointer + 1)) ||
2395 (escaped_dot(pointer) && pointer[3] ==
'.');
2397 return escaped_dot(pointer) && escaped_dot(pointer + 3);
2402 static constexpr auto single_dot = [](
const CharT*
const pointer,
const std::size_t len)
constexpr ->
bool {
2404 case 1:
return pointer[0] ==
'.';
2405 case 3:
return escaped_dot(pointer);
2406 default:
return false;
2411 auto pointer = first;
2413 const auto end_of_segment = urls.is_special_scheme()
2414 ? std::find_if(pointer, last, detail::is_slash<CharT>)
2415 : std::find(pointer, last,
'/');
2418 const std::size_t len = end_of_segment - pointer;
2419 const bool is_last = end_of_segment == last;
2422 if (double_dot(pointer, len)) {
2423 urls.shorten_path();
2424 if (is_last) urls.append_empty_path_segment();
2425 }
else if (single_dot(pointer, len)) {
2426 if (is_last) urls.append_empty_path_segment();
2429 urls.is_file_scheme() &&
2430 urls.is_empty_path() &&
2431 detail::is_windows_drive(pointer[0], pointer[1]))
2434 std::string& str_path = urls.start_path_segment();
2435 str_path +=
static_cast<char>(pointer[0]);
2437 urls.save_path_segment();
2440 std::string& str_path = urls.start_path_segment();
2441 do_path_segment(pointer, end_of_segment, str_path);
2442 urls.save_path_segment();
2444 pointer = end_of_segment;
2449 pointer = end_of_segment + 1;
2453template <
typename CharT>
2454inline void url_parser::do_path_segment(
const CharT* pointer,
const CharT* last, std::string& output) {
2455 using UCharT = std::make_unsigned_t<CharT>;
2458 while (pointer < last) {
2460 const auto uch =
static_cast<UCharT
>(*pointer);
2463 detail::append_utf8_percent_encoded_char(pointer, last, output);
2466 const auto uc =
static_cast<unsigned char>(uch);
2468 detail::append_percent_encoded_byte(uc, output);
2470 output.push_back(uc);
2476template <
typename CharT>
2477inline void url_parser::do_opaque_path(
const CharT* pointer,
const CharT* last, std::string& output) {
2478 using UCharT = std::make_unsigned_t<CharT>;
2487 if (pointer != last) {
2490 const bool ends_with_space = *(last - 1) ==
' ';
2491 if (ends_with_space)
2493 while (pointer < last) {
2495 const auto uch =
static_cast<UCharT
>(*pointer);
2498 detail::append_utf8_percent_encoded_char(pointer, last, output);
2501 const auto uc =
static_cast<unsigned char>(uch);
2503 detail::append_percent_encoded_byte(uc, output);
2505 output.push_back(uc);
2510 if (ends_with_space)
2511 output.append(
"%20");
2520inline std::string_view url::get_path_first_string(std::size_t len)
const UPA_LIFETIMEBOUND {
2521 std::string_view pathv = get_part_view(PATH);
2522 if (pathv.empty() || has_opaque_path())
2525 pathv.remove_prefix(1);
2526 if (pathv.length() == len || (pathv.length() > len && pathv[len] ==
'/')) {
2527 return { pathv.data(), len };
2534inline bool url::get_path_rem_last(std::size_t& path_end, std::size_t& path_segment_count)
const {
2535 if (path_segment_count_ > 0) {
2537 const char*
const first = norm_url_.data() + part_end_[
url::PATH-1];
2538 const char*
const last = norm_url_.data() + part_end_[
url::PATH];
2539 const char* it = detail::find_last(first, last,
'/');
2540 if (it == last) it = first;
2542 path_end = it - norm_url_.data();
2543 path_segment_count = path_segment_count_ - 1;
2551inline bool url::get_shorten_path(std::size_t& path_end, std::size_t& path_segment_count)
const {
2553 if (path_segment_count_ == 0)
2556 const std::string_view path1 = get_path_first_string(2);
2557 if (path1.length() == 2 &&
2558 detail::is_normalized_windows_drive(path1[0], path1[1]))
2562 return get_path_rem_last(path_end, path_segment_count);
2570inline void url_serializer::shorten_path() {
2572 if (url_.get_shorten_path(url_.part_end_[
url::PATH], url_.path_segment_count_))
2573 url_.norm_url_.resize(url_.part_end_[
url::PATH]);
2578inline std::string& url_serializer::start_scheme() {
2579 url_.norm_url_.clear();
2580 return url_.norm_url_;
2583inline void url_serializer::save_scheme() {
2584 set_scheme(url_.norm_url_.length());
2585 url_.norm_url_.push_back(
':');
2591 for (
int ind = t1; ind < t2; ++ind)
2592 url_.part_end_[ind] = offset;
2595inline std::string& url_serializer::start_part(
url::PartType new_pt) {
2597 auto fill_start_pt =
static_cast<url::PartType>(
static_cast<int>(last_pt_)+1);
2602 url_.norm_url_.append(
"//");
2606 url_.norm_url_ +=
':';
2615 url_.norm_url_ +=
'@';
2622 return url_.norm_url_;
2627 fill_parts_offset(fill_start_pt, new_pt, url_.norm_url_.length());
2631 url_.norm_url_ +=
':';
2634 url_.norm_url_ +=
'?';
2637 url_.norm_url_ +=
'#';
2642 assert(last_pt_ < new_pt || (last_pt_ == new_pt && is_empty(last_pt_)));
2645 return url_.norm_url_;
2648inline void url_serializer::save_part() {
2649 url_.part_end_[last_pt_] = url_.norm_url_.length();
2657inline void url_serializer::append_empty_path_segment() {
2658 start_path_segment();
2659 save_path_segment();
2662inline std::string& url_serializer::start_path_segment() {
2664 std::string& str_path = start_part(
url::PATH);
2669inline void url_serializer::save_path_segment() {
2671 url_.path_segment_count_++;
2674inline void url_serializer::commit_path() {
2676 adjust_path_prefix();
2679inline void url_serializer::adjust_path_prefix() {
2682 std::string_view new_prefix;
2683 if (is_null(
url::HOST) && url_.path_segment_count_ > 1) {
2684 const auto pathname = get_part_view(
url::PATH);
2685 if (pathname.length() > 1 && pathname[0] ==
'/' && pathname[1] ==
'/')
2686 new_prefix = {
"/.", 2 };
2692inline std::string& url_serializer::start_path_string() {
2696inline void url_serializer::save_path_string() {
2697 assert(url_.path_segment_count_ == 0);
2702inline void url_serializer::set_empty_host() {
2708inline void url_serializer::empty_host() {
2714 url_.norm_url_.resize(host_end);
2721inline std::string& url_serializer::hostStart() {
2725inline void url_serializer::hostDone(
HostType ht) {
2739 if (!need_save())
return;
2764 url::HOST_FLAG | url::HOST_TYPE_MASK,
2767 url::PATH_FLAG | url::OPAQUE_PATH_FLAG,
2774 for (
int ind = t1; ind <= t2; ++ind) {
2775 mask |= kPartFlagMask[ind];
2777 url_.flags_ = (url_.flags_ & ~mask) | (src.flags_ & mask);
2782 for (; ilast >= ifirst; --ilast) {
2783 if (src.part_end_[ilast])
2786 if (ifirst <= ilast) {
2789 std::string& norm_url = start_part(ifirst);
2792 std::size_t lastp_end = src.part_end_[ilast];
2794 std::size_t segment_count = src.path_segment_count_;
2797 (src.*pathOpFn)(lastp_end, segment_count);
2798 url_.path_segment_count_ = segment_count;
2800 url_.path_segment_count_ = src.path_segment_count_;
2803 const std::size_t offset = src.part_end_[ifirst - 1] + detail::kPartStart[ifirst];
2804 const char*
const first = src.norm_url_.data() + offset;
2805 const char*
const last = src.norm_url_.data() + lastp_end;
2807 const auto delta = util::checked_diff<std::ptrdiff_t>(norm_url.length(), offset);
2809 norm_url.append(first, last);
2811 for (
int ind = ifirst; ind < ilast; ++ind) {
2813 url_.part_end_[ind] = src.part_end_[ind] + delta;
2816 url_.part_end_[ilast] = lastp_end + delta;
2824inline std::size_t url_serializer::get_part_pos(
const url::PartType pt)
const {
2825 return pt >
url::SCHEME ? url_.part_end_[pt - 1] : 0;
2828inline std::size_t url_serializer::get_part_len(
const url::PartType pt)
const {
2829 return url_.part_end_[pt] - url_.part_end_[pt - 1];
2832inline void url_serializer::replace_part(
const url::PartType new_pt,
const char* str,
const std::size_t len) {
2833 replace_part(new_pt, str, len, new_pt, 0);
2836inline void url_serializer::replace_part(
const url::PartType last_pt,
const char* str,
const std::size_t len,
2839 const std::size_t b = get_part_pos(first_pt);
2840 const std::size_t l = url_.part_end_[last_pt] - b;
2841 url_.norm_url_.replace(b, l, str, len);
2842 std::fill(std::begin(url_.part_end_) + first_pt, std::begin(url_.part_end_) + last_pt, b + len0);
2844 const auto diff = util::checked_diff<std::ptrdiff_t>(len, l);
2846 for (
auto it = std::begin(url_.part_end_) + last_pt; it != std::end(url_.part_end_); ++it) {
2847 if (*it == 0)
break;
2849 *it =
static_cast<std::ptrdiff_t
>(*it) + diff;
2860inline void url_setter::reserve(std::size_t new_cap) {
2861 util::reserve(strp_, new_cap);
2866inline std::string& url_setter::start_scheme() {
2870inline void url_setter::save_scheme() {
2871 replace_part(
url::SCHEME, strp_.data(), strp_.length());
2872 set_scheme(strp_.length());
2877inline std::string& url_setter::start_part(
url::PartType new_pt) {
2880 if (url_.part_end_[new_pt]) {
2882 if (new_pt <
url::FRAGMENT && url_.part_end_[new_pt] < url_.norm_url_.length()) {
2905 last_pt_ =
static_cast<url::PartType>(
static_cast<int>(new_pt) - 1);
2906 url_.norm_url_.resize(url_.part_end_[last_pt_]);
2907 url_.part_end_[new_pt] = 0;
2909 for (
auto pt =
static_cast<int>(new_pt) + 1; pt <=
url::FRAGMENT && url_.part_end_[pt]; ++pt)
2910 url_.part_end_[pt] = 0;
2912 last_pt_ = find_last_part(new_pt);
2916 return url_serializer::start_part(new_pt);
2919inline void url_setter::save_part() {
2926 replace_part(
url::HOST, strp_.data(), strp_.length());
2928 const bool empty_val = strp_.length() <= detail::kPartStart[curr_pt_];
2932 if (!empty_val && !has_credentials()) {
2935 replace_part(
url::HOST_START, strp_.data(), strp_.length(), curr_pt_, strp_.length() - 1);
2946 replace_part(curr_pt_, strp_.data(), strp_.length());
2953 url_serializer::save_part();
2958 if (url_.part_end_[pt]) {
2959 replace_part(pt,
"", 0);
2960 url_.flags_ &= ~(1u << pt);
2965 if (url_.part_end_[pt]) {
2966 replace_part(pt,
"", 0);
2970inline void url_setter::empty_host() {
2975inline std::string& url_setter::start_path_segment() {
2981inline void url_setter::save_path_segment() {
2982 path_seg_end_.push_back(strp_.length());
2985inline void url_setter::commit_path() {
2987 for (
int ind =
url::PATH; ind > 0; --ind) {
2988 if (url_.part_end_[ind])
break;
2989 url_.part_end_[ind] = url_.norm_url_.length();
2992 replace_part(
url::PATH, strp_.data(), strp_.length());
2993 url_.path_segment_count_ = path_seg_end_.size();
2996 adjust_path_prefix();
3001inline void url_setter::shorten_path() {
3002 if (path_seg_end_.size() == 1) {
3003 if (is_file_scheme() && strp_.length() == 3 &&
3004 detail::is_normalized_windows_drive(strp_[1], strp_[2]))
3006 path_seg_end_.pop_back();
3008 }
else if (path_seg_end_.size() >= 2) {
3009 path_seg_end_.pop_back();
3010 strp_.resize(path_seg_end_.back());
3014inline bool url_setter::is_empty_path()
const {
3015 assert(!url_.has_opaque_path());
3017 return path_seg_end_.empty();
3021 for (
int ind = pt; ind > 0; --ind)
3022 if (url_.part_end_[ind])
3035template <
typename CharT>
3036inline const CharT* is_unc_path(
const CharT* first,
const CharT* last)
3039 std::size_t path_components_count = 0;
3040 const CharT* end_of_share_name =
nullptr;
3041 const auto* start = first;
3042 while (start != last) {
3043 const auto* pcend = std::find_if(start, last, detail::is_windows_slash<CharT>);
3048 if (std::find(start, pcend,
'\0') != pcend)
3051 ++path_components_count;
3053 switch (path_components_count) {
3056 switch (pcend - start) {
3060 if (start[0] ==
'?' || start[0] ==
'.')
3065 if (detail::is_windows_drive(start[0], start[1]))
3070 end_of_share_name = pcend;
3076 switch (pcend - start) {
3078 if (start[0] ==
'.')
3082 if (start[0] ==
'.' && start[1] ==
'.')
3087 end_of_share_name = pcend;
3091 if (pcend == last)
break;
3094 return end_of_share_name;
3103template <
typename CharT,
typename IsSlash>
3104constexpr bool has_dot_dot_segment(
const CharT* first,
const CharT* last, IsSlash is_slash) {
3105 if (last - first >= 2) {
3106 const auto* ptr = first;
3107 const auto* end = last - 1;
3108 while ((ptr = std::char_traits<CharT>::find(ptr, end - ptr,
'.')) !=
nullptr) {
3109 if (ptr[1] ==
'.' &&
3110 (ptr == first || is_slash(*(ptr - 1))) &&
3111 (last - ptr == 2 || is_slash(ptr[2])))
3135[[nodiscard]]
inline bool equals(
const url& lhs,
const url& rhs,
bool exclude_fragments =
false) {
3141 return lhs.norm_url_ == rhs.norm_url_;
3153 return os <<
url.norm_url_;
3201template <
class StrT, enable_if_str_arg_t<StrT> = 0>
3203 using CharT = str_arg_char_t<StrT>;
3204 const auto inp = make_str_arg(str);
3205 const auto* first = inp.begin();
3206 const auto* last = inp.end();
3208 if (first == last) {
3212 const auto* pointer = first;
3213 const auto* start_of_check = first;
3216 std::string str_url(
"file://");
3219 if (!detail::is_posix_slash(*first))
3221 if (detail::has_dot_dot_segment(start_of_check, last, detail::is_posix_slash<CharT>))
3227 bool is_unc =
false;
3232 if (last - pointer >= 2 &&
3233 detail::is_windows_slash(pointer[0]) &&
3234 detail::is_windows_slash(pointer[1])) {
3238 if (last - pointer >= 2 &&
3239 (pointer[0] ==
'?' || pointer[0] ==
'.') &&
3240 detail::is_windows_slash(pointer[1])) {
3243 if (last - pointer >= 4 &&
3244 (pointer[0] | 0x20) ==
'u' &&
3245 (pointer[1] | 0x20) ==
'n' &&
3246 (pointer[2] | 0x20) ==
'c' &&
3247 detail::is_windows_slash(pointer[3])) {
3256 start_of_check = is_unc
3257 ? detail::is_unc_path(pointer, last)
3258 : detail::is_windows_os_drive_absolute_path(pointer, last);
3259 if (start_of_check ==
nullptr ||
3260 detail::has_dot_dot_segment(start_of_check, last, detail::is_windows_slash<CharT>))
3263 if (!is_unc) str_url.push_back(
'/');
3267 if (util::contains_null(start_of_check, last))
3271 detail::append_utf8_percent_encoded(pointer, last, *no_encode_set, str_url);
3272 return url(str_url);
3304 const auto hostname = file_url.
hostname();
3305 const bool is_host = !hostname.empty();
3314 detail::append_percent_decoded(file_url.
pathname(), path);
3319 if (hostname ==
".")
3322 path.append(
"\\\\");
3326 std::replace_copy(std::next(hostname.begin()), std::prev(hostname.end()),
3327 std::back_inserter(path),
':',
'-');
3328 path.append(
".ipv6-literal.net");
3330 path.append(hostname);
3335 const auto start =
static_cast<std::ptrdiff_t
>(path.length());
3336 detail::append_percent_decoded(file_url.
pathname(), path);
3337 std::replace(std::next(path.begin(), start), path.end(),
'/',
'\\');
3340 if (!detail::is_unc_path(path.data() + 2, path.data() + path.length()))
3343 if (detail::pathname_has_windows_os_drive(path)) {
3345 if (path.length() == 2)
3346 path.push_back(
'\\');
3352 const auto count_leading_slashes = std::find_if(
3354 path.data() + std::min(
static_cast<std::size_t
>(4), path.length()),
3355 [](
char c) { return c !=
'\\'; }) - path.data();
3356 if (count_leading_slashes == 3)
3358 else if (count_leading_slashes != 2)
3360 if (!detail::is_unc_path(path.data() + 2, path.data() + path.length()))
3367 if (util::contains_null(path.begin(), path.end()))
3383 return { util::to_string_view<char8_t>(path_str.data(), path_str.size()),
3384 std::filesystem::path::native_format };
3407 constexpr auto sover_mask =
static_cast<std::uint32_t
>(-1) ^
3408 static_cast<std::uint32_t
>(0xFF);