Skip to content
89 changes: 62 additions & 27 deletions include/ada/url-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,40 +185,75 @@ constexpr void url::copy_scheme(const ada::url& u) {
type = u.type;
}

namespace detail {
// Grow string to n bytes without requiring value-init of new chars when the
// platform provides that API. Not noexcept: allocation may throw bad_alloc.
ada_really_inline void string_resize_uninitialized(std::string& s, size_t n) {
#if defined(__cpp_lib_string_resize_and_overwrite)
s.resize_and_overwrite(
n, [](char*, std::size_t count) noexcept { return count; });
#elif defined(_LIBCPP_VERSION) && defined(__APPLE__)
// Apple libc++ public extension; not available on all libc++ / libstdc++.
s.__resize_default_init(n);
#else
s.resize(n);
#endif
}
} // namespace detail

[[nodiscard]] ada_really_inline std::string url::get_href() const {
if (is_special() && host.has_value() && username.empty() &&
password.empty() && !port.has_value()) [[likely]] {
const std::string_view scheme = ada::scheme::details::is_special_list[type];
// Hot path: special URL, no credentials, no port (covers almost all
// benchdata / production absolute URLs).
if (host.has_value() && username.empty() && password.empty() &&
!port.has_value() && type != ada::scheme::type::NOT_SPECIAL) [[likely]] {
// Hardcode common schemes to avoid table load + size branch.
const char* scheme_ptr;
size_t scheme_len;
if (type == ada::scheme::type::HTTPS) [[likely]] {
scheme_ptr = "https";
scheme_len = 5;
} else if (type == ada::scheme::type::HTTP) {
scheme_ptr = "http";
scheme_len = 4;
} else {
const std::string_view scheme =
ada::scheme::details::is_special_list[type];
scheme_ptr = scheme.data();
scheme_len = scheme.size();
}
const size_t host_size = host->size();
const size_t path_size = path.size();
const size_t query_size = query.has_value() ? query->size() : 0;
const size_t hash_size = hash.has_value() ? hash->size() : 0;
const size_t total = scheme.size() + 3 + host_size + path_size +
(query.has_value() ? query_size + 1 : 0) +
(hash.has_value() ? hash_size + 1 : 0);
std::string output(total, '\0');
char* p = output.data();
std::memcpy(p, scheme.data(), scheme.size());
p += scheme.size();
p[0] = ':';
p[1] = '/';
p[2] = '/';
p += 3;
const bool has_q = query.has_value();
const bool has_h = hash.has_value();
const size_t query_size = has_q ? query->size() : 0;
const size_t hash_size = has_h ? hash->size() : 0;
const size_t total = scheme_len + 3 + host_size + path_size +
(has_q ? query_size + 1 : 0) +
(has_h ? hash_size + 1 : 0);
std::string output;
detail::string_resize_uninitialized(output, total);
char* d = output.data();
std::memcpy(d, scheme_ptr, scheme_len);
d += scheme_len;
d[0] = ':';
d[1] = '/';
d[2] = '/';
d += 3;
// NOLINTNEXTLINE(bugprone-not-null-terminated-result)
std::memcpy(p, host->data(), host_size);
p += host_size;
std::memcpy(p, path.data(), path_size);
p += path_size;
if (query.has_value()) {
*p++ = '?';
std::memcpy(d, host->data(), host_size);
d += host_size;
std::memcpy(d, path.data(), path_size);
d += path_size;
if (has_q) {
*d++ = '?';
// NOLINTNEXTLINE(bugprone-not-null-terminated-result)
std::memcpy(p, query->data(), query_size);
p += query_size;
std::memcpy(d, query->data(), query_size);
d += query_size;
}
if (hash.has_value()) {
*p++ = '#';
if (has_h) {
*d++ = '#';
// NOLINTNEXTLINE(bugprone-not-null-terminated-result)
std::memcpy(p, hash->data(), hash_size);
std::memcpy(d, hash->data(), hash_size);
}
return output;
}
Expand Down
3 changes: 3 additions & 0 deletions src/implementation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ std::optional<bool> try_can_parse_absolute_fast(
template <class result_type>
ada_warn_unused tl::expected<result_type, errors> parse(
std::string_view input, const result_type* base_url) {
// Single try_parse lives inside parse_url_impl. Do not call try_parse here
// as well: IPv4/IPv6 (and other non-simple) inputs would pay the fast-path
// reject twice and show large CodSpeed regressions on those benches.
result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
if (!u.is_valid) {
return tl::unexpected(errors::type_error);
Expand Down
Loading
Loading