include/boost/url/impl/url_base.hpp

99.4% Lines (1529 / 1538) 100.0% Functions (80 / 80)
url_base.hpp
f(x) Functions (80)
Function Calls Lines Blocks
boost::urls::url_base::op_t::~op_t() :55 18599x 100.0% 100.0% boost::urls::url_base::op_t::op_t(boost::urls::url_base&, boost::core::basic_string_view<char>*, boost::core::basic_string_view<char>*) :65 18599x 100.0% 100.0% boost::urls::url_base::op_t::move(char*, char const*, unsigned long) :80 9461x 100.0% 100.0% boost::urls::url_base::url_base(boost::urls::detail::url_impl const&) :105 1647x 100.0% 100.0% boost::urls::url_base::reserve_impl(unsigned long) :114 308x 100.0% 100.0% boost::urls::url_base::copy(boost::urls::url_view_base const&) :126 5300x 100.0% 91.0% boost::urls::url_base::set_scheme(boost::core::basic_string_view<char>) :155 379x 100.0% 100.0% boost::urls::url_base::set_scheme_id(boost::urls::scheme) :165 182x 100.0% 100.0% boost::urls::url_base::remove_scheme() :178 56x 100.0% 95.0% boost::urls::url_base::set_encoded_authority(boost::urls::pct_string_view) :298 124x 100.0% 95.0% boost::urls::url_base::remove_authority() :332 263x 100.0% 90.0% boost::urls::url_base::set_userinfo(boost::core::basic_string_view<char>) :376 225x 87.5% 81.0% boost::urls::url_base::set_encoded_userinfo(boost::urls::pct_string_view) :427 53x 100.0% 87.0% boost::urls::url_base::remove_userinfo() :483 214x 100.0% 100.0% boost::urls::url_base::set_user(boost::core::basic_string_view<char>) :502 241x 100.0% 85.0% boost::urls::url_base::set_encoded_user(boost::urls::pct_string_view) :523 148x 100.0% 82.0% boost::urls::url_base::set_password(boost::core::basic_string_view<char>) :548 231x 100.0% 85.0% boost::urls::url_base::set_encoded_password(boost::urls::pct_string_view) :569 141x 100.0% 82.0% boost::urls::url_base::remove_password() :593 19x 100.0% 100.0% boost::urls::url_base::set_host(boost::core::basic_string_view<char>) :645 404x 100.0% 76.0% boost::urls::url_base::set_encoded_host(boost::urls::pct_string_view) :722 221x 100.0% 72.0% boost::urls::url_base::set_host_address(boost::core::basic_string_view<char>) :802 13x 100.0% 75.0% boost::urls::url_base::set_encoded_host_address(boost::urls::pct_string_view) :872 11x 100.0% 70.0% boost::urls::url_base::set_host_ipv4(boost::urls::ipv4_address const&) :949 203x 100.0% 89.0% boost::urls::url_base::set_host_ipv6(boost::urls::ipv6_address const&) :971 5x 100.0% 100.0% boost::urls::url_base::set_zone_id(boost::core::basic_string_view<char>) :981 6x 100.0% 100.0% boost::urls::url_base::set_encoded_zone_id(boost::urls::pct_string_view) :990 6x 100.0% 100.0% boost::urls::url_base::set_host_ipv6_and_zone_id(boost::urls::ipv6_address const&, boost::core::basic_string_view<char>) :999 14x 100.0% 91.0% boost::urls::url_base::set_host_ipv6_and_encoded_zone_id(boost::urls::ipv6_address const&, boost::urls::pct_string_view) :1038 73x 100.0% 91.0% boost::urls::url_base::set_host_ipvfuture(boost::core::basic_string_view<char>) :1076 7x 100.0% 100.0% boost::urls::url_base::set_host_name(boost::core::basic_string_view<char>) :1099 4x 100.0% 91.0% boost::urls::url_base::set_encoded_host_name(boost::urls::pct_string_view) :1134 4x 100.0% 89.0% boost::urls::url_base::set_port_number(unsigned short) :1171 215x 100.0% 81.0% boost::urls::url_base::set_port(boost::core::basic_string_view<char>) :1189 389x 100.0% 100.0% boost::urls::url_base::remove_port() :1210 206x 100.0% 100.0% boost::urls::url_base::remove_origin() :1227 14x 100.0% 100.0% boost::urls::url_base::set_path_absolute(bool) :1244 52x 100.0% 94.0% boost::urls::url_base::set_path(boost::core::basic_string_view<char>) :1326 298x 100.0% 89.0% boost::urls::url_base::set_encoded_path(boost::urls::pct_string_view) :1446 300x 100.0% 92.0% boost::urls::url_base::segments() :1561 1146x 100.0% 100.0% boost::urls::url_base::encoded_segments() :1569 522x 100.0% 100.0% boost::urls::url_base::set_query(boost::core::basic_string_view<char>) :1583 169x 100.0% 59.0% boost::urls::url_base::set_encoded_query(boost::urls::pct_string_view) :1596 179x 100.0% 90.0% boost::urls::url_base::params() :1653 986x 100.0% 100.0% boost::urls::url_base::params(boost::urls::encoding_opts) :1664 4x 100.0% 100.0% boost::urls::url_base::encoded_params() :1672 77x 100.0% 100.0% boost::urls::url_base::set_params(std::initializer_list<boost::urls::param_view>, boost::urls::encoding_opts) :1680 1x 100.0% 100.0% boost::urls::url_base::set_encoded_params(std::initializer_list<boost::urls::param_pct_view>) :1691 1x 100.0% 100.0% boost::urls::url_base::remove_query() :1700 991x 100.0% 100.0% boost::urls::url_base::remove_fragment() :1718 408x 100.0% 100.0% boost::urls::url_base::set_fragment(boost::core::basic_string_view<char>) :1729 151x 100.0% 85.0% boost::urls::url_base::set_encoded_fragment(boost::urls::pct_string_view) :1754 183x 100.0% 82.0% boost::urls::url_base::resolve(boost::urls::url_view_base const&) :1786 517x 100.0% 96.0% void boost::urls::url_base::normalize_octets_impl<boost::urls::grammar::lut_chars, boost::urls::detail::empty_chars_t>(int, boost::urls::grammar::lut_chars const&, boost::urls::detail::empty_chars_t const&, boost::urls::url_base::op_t&) :1915 3211x 100.0% 96.0% void boost::urls::url_base::normalize_octets_impl<boost::urls::grammar::lut_chars>(int, boost::urls::grammar::lut_chars const&, boost::urls::url_base::op_t&) :1965 3211x 100.0% 100.0% boost::urls::url_base::normalize_scheme() :1977 263x 100.0% 100.0% boost::urls::url_base::normalize_authority() :1986 609x 100.0% 100.0% boost::urls::url_base::normalize_path() :2010 1112x 100.0% 91.0% boost::urls::url_base::normalize_path()::{lambda()#1}::operator()() const :2167 1112x 100.0% 100.0% boost::urls::url_base::normalize_query() :2358 264x 100.0% 100.0% boost::urls::url_base::normalize_fragment() :2369 261x 100.0% 100.0% boost::urls::url_base::normalize() :2380 260x 100.0% 100.0% boost::urls::url_base::check_invariants() const :2399 44237x 100.0% 86.0% boost::urls::url_base::resize_impl(int, unsigned long, boost::urls::url_base::op_t&) :2440 5543x 100.0% 100.0% boost::urls::url_base::resize_impl(int, int, unsigned long, boost::urls::url_base::op_t&) :2452 6278x 100.0% 100.0% boost::urls::url_base::shrink_impl(int, unsigned long, boost::urls::url_base::op_t&) :2488 232x 100.0% 100.0% boost::urls::url_base::shrink_impl(int, int, unsigned long, boost::urls::url_base::op_t&) :2500 2452x 100.0% 92.0% boost::urls::url_base::set_scheme_impl(boost::core::basic_string_view<char>, boost::urls::scheme) :2532 529x 100.0% 100.0% boost::urls::url_base::set_scheme_impl(boost::core::basic_string_view<char>, boost::urls::scheme)::{lambda()#1}::operator()() const :2547 481x 96.6% 92.0% boost::urls::url_base::set_user_impl(unsigned long, boost::urls::url_base::op_t&) :2590 399x 100.0% 100.0% boost::urls::url_base::set_password_impl(unsigned long, boost::urls::url_base::op_t&) :2628 379x 100.0% 100.0% boost::urls::url_base::set_userinfo_impl(unsigned long, boost::urls::url_base::op_t&) :2671 278x 100.0% 100.0% boost::urls::url_base::set_host_impl(unsigned long, boost::urls::url_base::op_t&) :2701 742x 100.0% 100.0% boost::urls::url_base::set_port_impl(unsigned long, boost::urls::url_base::op_t&) :2740 605x 100.0% 100.0% boost::urls::url_base::set_path_impl(unsigned long, boost::urls::url_base::op_t&) :2778 598x 100.0% 100.0% boost::urls::url_base::first_segment() const :2796 268x 94.1% 80.0% boost::urls::url_base::edit_segments(boost::urls::detail::segments_iter_impl const&, boost::urls::detail::segments_iter_impl const&, boost::urls::detail::any_segments_iter&&, int) :2821 2315x 98.1% 84.0% boost::urls::url_base::edit_params(boost::urls::detail::params_iter_impl const&, boost::urls::detail::params_iter_impl const&, boost::urls::detail::any_params_iter&&) :3148 1701x 98.7% 84.0% boost::urls::url_base::decoded_to_lower_impl(int) :3309 609x 100.0% 100.0% boost::urls::url_base::to_lower_impl(int) :3329 263x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/boostorg/url
9 //
10
11 #ifndef BOOST_URL_IMPL_URL_BASE_HPP
12 #define BOOST_URL_IMPL_URL_BASE_HPP
13
14 #include <boost/url/encode.hpp>
15 #include <boost/url/error.hpp>
16 #include <boost/url/host_type.hpp>
17 #include <boost/url/scheme.hpp>
18 #include <boost/url/url_view.hpp>
19 #include <boost/url/detail/any_params_iter.hpp>
20 #include <boost/url/detail/any_segments_iter.hpp>
21 #include <boost/url/detail/decode.hpp>
22 #include <boost/url/detail/encode.hpp>
23 #include <boost/url/detail/except.hpp>
24 #include <boost/url/detail/normalize.hpp>
25 #include <boost/url/detail/path.hpp>
26 #include <boost/url/detail/print.hpp>
27 #include <boost/url/grammar/ci_string.hpp>
28 #include <boost/url/rfc/authority_rule.hpp>
29 #include <boost/url/rfc/query_rule.hpp>
30 #include <boost/url/rfc/ipv6_address_rule.hpp>
31 #include <boost/url/rfc/detail/charsets.hpp>
32 #include <boost/url/rfc/detail/host_rule.hpp>
33 #include <boost/url/rfc/detail/ipvfuture_rule.hpp>
34 #include <boost/url/rfc/detail/path_rules.hpp>
35 #include <boost/url/rfc/detail/port_rule.hpp>
36 #include <boost/url/rfc/detail/scheme_rule.hpp>
37 #include <boost/url/rfc/detail/userinfo_rule.hpp>
38 #include <boost/url/grammar/parse.hpp>
39 #include <boost/url/detail/move_chars.hpp>
40 #include <cstring>
41 #include <iostream>
42 #include <stdexcept>
43 #include <utility>
44
45 namespace boost {
46 namespace urls {
47
48 //------------------------------------------------
49
50 // these objects help handle the cases
51 // where the user passes in strings that
52 // come from inside the url buffer.
53
54 inline
55 18599x url_base::
56 op_t::
57 ~op_t()
58 {
59 18599x if(old)
60 2575x u.cleanup(*this);
61 18599x u.check_invariants();
62 18599x }
63
64 inline
65 18599x url_base::
66 op_t::
67 op_t(
68 url_base& impl_,
69 core::string_view* s0_,
70 18599x core::string_view* s1_) noexcept
71 18599x : u(impl_)
72 18599x , s0(s0_)
73 18599x , s1(s1_)
74 {
75 18599x u.check_invariants();
76 18599x }
77
78 inline
79 void
80 9461x url_base::
81 op_t::
82 move(
83 char* dest,
84 char const* src,
85 std::size_t n) noexcept
86 {
87 9461x if(! n)
88 2523x return;
89 6938x if(s0)
90 {
91 4919x if(s1)
92 656x return detail::move_chars(
93 656x dest, src, n, *s0, *s1);
94 4263x return detail::move_chars(
95 4263x dest, src, n, *s0);
96 }
97 2019x detail::move_chars(
98 dest, src, n);
99 }
100
101 //------------------------------------------------
102
103 // construct reference
104 inline
105 1647x url_base::
106 url_base(
107 1647x detail::url_impl const& impl) noexcept
108 1647x : url_view_base(impl)
109 {
110 1647x }
111
112 inline
113 void
114 308x url_base::
115 reserve_impl(std::size_t n)
116 {
117 308x op_t op(*this);
118 308x reserve_impl(n, op);
119 307x if(s_)
120 305x s_[size()] = '\0';
121 308x }
122
123 // make a copy of u
124 inline
125 void
126 5300x url_base::
127 copy(url_view_base const& u)
128 {
129 5300x if (this == &u)
130 140x return;
131 5286x op_t op(*this);
132 5286x if(u.size() == 0)
133 {
134 126x clear();
135 126x return;
136 }
137 5160x reserve_impl(
138 u.size(), op);
139 5157x impl_ = u.impl();
140 5157x impl_.cs_ = s_;
141 5157x impl_.from_ = {from::url};
142 5157x std::memcpy(s_,
143 5157x u.data(), u.size());
144 5157x s_[size()] = '\0';
145 5286x }
146
147 //------------------------------------------------
148 //
149 // Scheme
150 //
151 //------------------------------------------------
152
153 inline
154 url_base&
155 379x url_base::
156 set_scheme(core::string_view s)
157 {
158 379x set_scheme_impl(
159 s, string_to_scheme(s));
160 362x return *this;
161 }
162
163 inline
164 url_base&
165 182x url_base::
166 set_scheme_id(urls::scheme id)
167 {
168 182x if(id == urls::scheme::unknown)
169 14x detail::throw_invalid_argument();
170 168x if(id == urls::scheme::none)
171 18x return remove_scheme();
172 150x set_scheme_impl(to_string(id), id);
173 119x return *this;
174 }
175
176 inline
177 url_base&
178 56x url_base::
179 remove_scheme()
180 {
181 56x op_t op(*this);
182 56x auto const sn = impl_.len(id_scheme);
183 56x if(sn == 0)
184 15x return *this;
185 41x auto const po = impl_.offset(id_path);
186 // The first segment as written, not as
187 // first_segment() returns it: a "./" prefix
188 // already keeps a colon in the next segment
189 // from looking like a scheme, and the colon
190 // encoding below starts at the path
191 41x core::string_view fseg = impl_.get(id_path);
192 41x fseg = fseg.substr(0, fseg.find('/'));
193 bool const encode_colon =
194 41x !has_authority() &&
195 24x impl_.nseg_ > 0 &&
196 79x s_[po] != '/' &&
197 14x fseg.contains(':');
198 41x if(!encode_colon)
199 {
200 // just remove the scheme
201 32x resize_impl(id_scheme, 0, op);
202 32x impl_.scheme_ = urls::scheme::none;
203 32x check_invariants();
204 32x return *this;
205 }
206 // encode any ":" in the first path segment
207 9x BOOST_ASSERT(sn >= 2);
208 9x auto pn = impl_.len(id_path);
209 9x std::size_t cn = 0;
210 46x for (char c: fseg)
211 37x cn += c == ':';
212 std::size_t new_size =
213 9x size() - sn + 2 * cn;
214 9x bool need_resize = new_size > size();
215 9x if (need_resize)
216 {
217 1x resize_impl(
218 1x id_path, pn + 2 * cn, op);
219 }
220 // move [id_scheme, id_path) left
221 9x op.move(
222 s_,
223 9x s_ + sn,
224 po - sn);
225 // move [id_path, id_query) left
226 9x auto qo = impl_.offset(id_query);
227 9x op.move(
228 9x s_ + po - sn,
229 9x s_ + po,
230 qo - po);
231 // move [id_query, id_end) left
232 9x op.move(
233 9x s_ + qo - sn + 2 * cn,
234 9x s_ + qo,
235 9x impl_.offset(id_end) - qo);
236
237 // adjust part offsets.
238 // (po and qo are invalidated)
239 9x if (need_resize)
240 {
241 1x impl_.adjust_left(id_user, id_end, sn);
242 }
243 else
244 {
245 8x impl_.adjust_left(id_user, id_path, sn);
246 8x impl_.adjust_left(id_query, id_end, sn - 2 * cn);
247 }
248 9x if (encode_colon)
249 {
250 // move the 2nd, 3rd, ... segments
251 9x auto begin = s_ + impl_.offset(id_path);
252 9x auto it = begin;
253 9x auto end = begin + pn;
254 46x while (it != end &&
255 40x *it != '/')
256 37x ++it;
257 // we don't need op here because this is
258 // an internal operation
259 9x std::memmove(it + (2 * cn), it, end - it);
260
261 // move 1st segment
262 9x auto src = s_ + impl_.offset(id_path) + pn;
263 9x auto dest = s_ + impl_.offset(id_query);
264 9x src -= end - it;
265 9x dest -= end - it;
266 9x pn -= end - it;
267 do {
268 37x --src;
269 37x --dest;
270 37x if (*src != ':')
271 {
272 25x *dest = *src;
273 }
274 else
275 {
276 // use uppercase as required by
277 // syntax-based normalization
278 12x *dest-- = 'A';
279 12x *dest-- = '3';
280 12x *dest = '%';
281 }
282 37x --pn;
283 37x } while (pn);
284 }
285 9x s_[size()] = '\0';
286 9x impl_.scheme_ = urls::scheme::none;
287 9x return *this;
288 56x }
289
290 //------------------------------------------------
291 //
292 // Authority
293 //
294 //------------------------------------------------
295
296 inline
297 url_base&
298 124x url_base::
299 set_encoded_authority(
300 pct_string_view s)
301 {
302 124x op_t op(*this, &detail::ref(s));
303 126x authority_view a = grammar::parse(
304 s, authority_rule
305 124x ).value(BOOST_URL_POS);
306 123x auto n = s.size() + 2;
307 auto const need_slash =
308 149x ! is_path_absolute() &&
309 26x impl_.len(id_path) > 0;
310 123x if(need_slash)
311 6x ++n;
312 123x auto dest = resize_impl(
313 id_user, id_path, n, op);
314 123x dest[0] = '/';
315 123x dest[1] = '/';
316 123x std::memcpy(dest + 2,
317 123x s.data(), s.size());
318 123x if(need_slash)
319 6x dest[n - 1] = '/';
320 123x impl_.apply_authority(a.u_);
321 123x if(need_slash)
322 {
323 6x impl_.adjust_right(
324 id_query, id_end, 1);
325 6x ++impl_.decoded_[id_path];
326 }
327 123x return *this;
328 124x }
329
330 inline
331 url_base&
332 263x url_base::
333 remove_authority()
334 {
335 263x if(! has_authority())
336 70x return *this;
337
338 193x op_t op(*this);
339 193x auto path = impl_.get(id_path);
340 193x bool const need_dot = path.starts_with("//");
341 193x if(need_dot)
342 {
343 // prepend "/.", can't throw
344 8x auto p = resize_impl(
345 id_user, id_path, 2, op);
346 8x p[0] = '/';
347 8x p[1] = '.';
348 8x impl_.split(id_user, 0);
349 8x impl_.split(id_pass, 0);
350 8x impl_.split(id_host, 0);
351 8x impl_.split(id_port, 0);
352 8x impl_.decoded_[id_path] += 2;
353 }
354 else
355 {
356 185x resize_impl(
357 id_user, id_path, 0, op);
358 }
359 193x impl_.decoded_[id_user] = 0;
360 193x impl_.decoded_[id_pass] = 0;
361 193x impl_.decoded_[id_host] = 0;
362 193x impl_.decoded_[id_port] = 0;
363 193x impl_.host_type_ =
364 urls::host_type::none;
365 193x return *this;
366 193x }
367
368 //------------------------------------------------
369 //
370 // Userinfo
371 //
372 //------------------------------------------------
373
374 inline
375 url_base&
376 225x url_base::
377 set_userinfo(
378 core::string_view s)
379 {
380 225x op_t op(*this, &s);
381 225x encoding_opts opt;
382 225x auto const n = encoded_size(
383 s, detail::userinfo_chars, opt);
384 225x auto dest = set_userinfo_impl(n, op);
385 225x encode(
386 dest,
387 n,
388 s,
389 detail::userinfo_chars,
390 opt);
391 225x auto const pos = impl_.get(
392 id_user, id_host
393 225x ).find_first_of(':');
394 225x if(pos != core::string_view::npos)
395 {
396 22x impl_.split(id_user, pos);
397 // find ':' in plain string
398 auto const pos2 =
399 22x s.find_first_of(':');
400 22x if(pos2 != core::string_view::npos)
401 {
402 // pos2 is the ':' index in plain input (user[:pass])
403 // decoded user is [0, pos2), decoded pass is (pos2, end].
404 22x impl_.decoded_[id_user] =
405 22x detail::to_size_type(pos2);
406 22x impl_.decoded_[id_pass] =
407 22x detail::to_size_type(s.size() - pos2 - 1);
408 }
409 else
410 {
411 ✗ impl_.decoded_[id_user] =
412 ✗ detail::to_size_type(s.size());
413 ✗ impl_.decoded_[id_pass] = 0;
414 }
415 }
416 else
417 {
418 203x impl_.decoded_[id_user] =
419 203x detail::to_size_type(s.size());
420 203x impl_.decoded_[id_pass] = 0;
421 }
422 225x return *this;
423 225x }
424
425 inline
426 url_base&
427 53x url_base::
428 set_encoded_userinfo(
429 pct_string_view s)
430 {
431 53x op_t op(*this, &detail::ref(s));
432 53x auto const pos = s.find_first_of(':');
433 53x if(pos != core::string_view::npos)
434 {
435 // user:pass
436 8x auto const s0 = s.substr(0, pos);
437 8x auto const s1 = s.substr(pos + 1);
438 auto const n0 =
439 8x detail::re_encoded_size_unsafe(
440 s0,
441 detail::user_chars);
442 auto const n1 =
443 8x detail::re_encoded_size_unsafe(s1,
444 detail::password_chars);
445 auto dest =
446 8x set_userinfo_impl(n0 + n1 + 1, op);
447 8x impl_.decoded_[id_user] =
448 8x detail::to_size_type(detail::re_encode_unsafe(
449 dest,
450 8x dest + n0,
451 s0,
452 detail::user_chars));
453 8x *dest++ = ':';
454 8x impl_.decoded_[id_pass] =
455 8x detail::to_size_type(detail::re_encode_unsafe(
456 dest,
457 8x dest + n1,
458 s1,
459 detail::password_chars));
460 8x impl_.split(id_user, 2 + n0);
461 }
462 else
463 {
464 // user
465 auto const n =
466 45x detail::re_encoded_size_unsafe(
467 s, detail::user_chars);
468 45x auto dest = set_userinfo_impl(n, op);
469 45x impl_.decoded_[id_user] =
470 90x detail::to_size_type(detail::re_encode_unsafe(
471 dest,
472 45x dest + n,
473 s,
474 detail::user_chars));
475 45x impl_.split(id_user, 2 + n);
476 45x impl_.decoded_[id_pass] = 0;
477 }
478 53x return *this;
479 53x }
480
481 inline
482 url_base&
483 214x url_base::
484 remove_userinfo() noexcept
485 {
486 214x if(impl_.len(id_pass) == 0)
487 138x return *this; // no userinfo
488
489 76x op_t op(*this);
490 // keep authority '//'
491 76x resize_impl(
492 id_user, id_host, 2, op);
493 76x impl_.decoded_[id_user] = 0;
494 76x impl_.decoded_[id_pass] = 0;
495 76x return *this;
496 76x }
497
498 //------------------------------------------------
499
500 inline
501 url_base&
502 241x url_base::
503 set_user(core::string_view s)
504 {
505 241x op_t op(*this, &s);
506 241x encoding_opts opt;
507 241x auto const n = encoded_size(
508 s, detail::user_chars, opt);
509 241x auto dest = set_user_impl(n, op);
510 241x encode_unsafe(
511 dest,
512 n,
513 s,
514 detail::user_chars,
515 opt);
516 241x impl_.decoded_[id_user] =
517 241x detail::to_size_type(s.size());
518 241x return *this;
519 241x }
520
521 inline
522 url_base&
523 148x url_base::
524 set_encoded_user(
525 pct_string_view s)
526 {
527 148x op_t op(*this, &detail::ref(s));
528 auto const n =
529 148x detail::re_encoded_size_unsafe(
530 s, detail::user_chars);
531 148x auto dest = set_user_impl(n, op);
532 148x impl_.decoded_[id_user] =
533 296x detail::to_size_type(detail::re_encode_unsafe(
534 dest,
535 148x dest + n,
536 s,
537 detail::user_chars));
538 148x BOOST_ASSERT(
539 impl_.decoded_[id_user] ==
540 s.decoded_size());
541 148x return *this;
542 148x }
543
544 //------------------------------------------------
545
546 inline
547 url_base&
548 231x url_base::
549 set_password(core::string_view s)
550 {
551 231x op_t op(*this, &s);
552 231x encoding_opts opt;
553 231x auto const n = encoded_size(
554 s, detail::password_chars, opt);
555 231x auto dest = set_password_impl(n, op);
556 231x encode_unsafe(
557 dest,
558 n,
559 s,
560 detail::password_chars,
561 opt);
562 231x impl_.decoded_[id_pass] =
563 231x detail::to_size_type(s.size());
564 231x return *this;
565 231x }
566
567 inline
568 url_base&
569 141x url_base::
570 set_encoded_password(
571 pct_string_view s)
572 {
573 141x op_t op(*this, &detail::ref(s));
574 auto const n =
575 141x detail::re_encoded_size_unsafe(
576 s,
577 detail::password_chars);
578 141x auto dest = set_password_impl(n, op);
579 141x impl_.decoded_[id_pass] =
580 282x detail::to_size_type(detail::re_encode_unsafe(
581 dest,
582 141x dest + n,
583 s,
584 detail::password_chars));
585 141x BOOST_ASSERT(
586 impl_.decoded_[id_pass] ==
587 s.decoded_size());
588 141x return *this;
589 141x }
590
591 inline
592 url_base&
593 19x url_base::
594 remove_password() noexcept
595 {
596 19x auto const n = impl_.len(id_pass);
597 19x if(n < 2)
598 12x return *this; // no password
599
600 7x op_t op(*this);
601 // clear password, retain '@'
602 auto dest =
603 7x resize_impl(id_pass, 1, op);
604 7x dest[0] = '@';
605 7x impl_.decoded_[id_pass] = 0;
606 7x return *this;
607 7x }
608
609 //------------------------------------------------
610 //
611 // Host
612 //
613 //------------------------------------------------
614 /*
615 host_type host_type() // ipv4, ipv6, ipvfuture, name
616
617 std::string host() // return encoded_host().decode()
618 pct_string_view encoded_host() // return host part, as-is
619 std::string host_address() // return encoded_host_address().decode()
620 pct_string_view encoded_host_address() // ipv4, ipv6, ipvfut, or encoded name, no brackets
621
622 ipv4_address host_ipv4_address() // return ipv4_address or {}
623 ipv6_address host_ipv6_address() // return ipv6_address or {}
624 core::string_view host_ipvfuture() // return ipvfuture or {}
625 std::string host_name() // return decoded name or ""
626 pct_string_view encoded_host_name() // return encoded host name or ""
627
628 --------------------------------------------------
629
630 set_host( core::string_view ) // set host part from plain text
631 set_encoded_host( pct_string_view ) // set host part from encoded text
632 set_host_address( core::string_view ) // set host from ipv4, ipv6, ipvfut, or plain reg-name string
633 set_encoded_host_address( pct_string_view ) // set host from ipv4, ipv6, ipvfut, or encoded reg-name string
634
635 set_host_ipv4( ipv4_address ) // set ipv4
636 set_host_ipv6( ipv6_address ) // set ipv6
637 set_host_ipvfuture( core::string_view ) // set ipvfuture
638 set_host_name( core::string_view ) // set name from plain
639 set_encoded_host_name( pct_string_view ) // set name from encoded
640 */
641
642 // set host part from plain text
643 inline
644 url_base&
645 404x url_base::
646 set_host(
647 core::string_view s)
648 {
649 404x if( s.size() > 2 &&
650 456x s.front() == '[' &&
651 52x s.back() == ']')
652 {
653 // IP-literal
654 52x if (s[1] != 'v')
655 {
656 // IPv6-address
657 51x auto innersv = s.substr(1, s.size() - 2);
658 51x auto innerit = innersv.begin();
659 51x auto endit = innersv.end();
660 51x auto rv = grammar::parse(
661 innerit,
662 endit,
663 ipv6_address_rule);
664 51x if(rv)
665 {
666 51x if (innerit == endit)
667 {
668 47x set_host_ipv6_and_encoded_zone_id(*rv, {});
669 51x return *this;
670 }
671 // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
672 4x auto chars_left = endit - innerit;
673 8x if (chars_left >= 2 &&
674 4x *innerit++ == '%')
675 {
676 4x core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
677 4x set_host_ipv6_and_zone_id(*rv, zone_id_str);
678 4x return *this;
679 }
680 }
681 }
682 else
683 {
684 // IPvFuture
685 1x auto rv = grammar::parse(
686 1x s.substr(1, s.size() - 2),
687 detail::ipvfuture_rule);
688 1x if(rv)
689 1x return set_host_ipvfuture(rv->str);
690 }
691 }
692 352x else if(s.size() >= 7) // "0.0.0.0"
693 {
694 // IPv4-address
695 348x auto rv = parse_ipv4_address(s);
696 348x if(rv)
697 189x return set_host_ipv4(*rv);
698 }
699
700 // reg-name
701 163x op_t op(*this, &s);
702 163x encoding_opts opt;
703 163x auto const n = encoded_size(
704 s, detail::host_chars, opt);
705 163x auto dest = set_host_impl(n, op);
706 163x encode(
707 dest,
708 163x impl_.get(id_path).data() - dest,
709 s,
710 detail::host_chars,
711 opt);
712 163x impl_.decoded_[id_host] =
713 163x detail::to_size_type(s.size());
714 163x impl_.host_type_ =
715 urls::host_type::name;
716 163x return *this;
717 163x }
718
719 // set host part from encoded text
720 inline
721 url_base&
722 221x url_base::
723 set_encoded_host(
724 pct_string_view s)
725 {
726 221x if( s.size() > 2 &&
727 241x s.front() == '[' &&
728 20x s.back() == ']')
729 {
730 // IP-literal
731 20x if (s[1] != 'v')
732 {
733 // IPv6-address
734 19x auto innersv = s.substr(1, s.size() - 2);
735 19x auto innerit = innersv.begin();
736 19x auto endit = innersv.end();
737 19x auto rv = grammar::parse(
738 innerit,
739 endit,
740 ipv6_address_rule);
741 19x if(rv)
742 {
743 11x if (innerit == endit)
744 {
745 5x set_host_ipv6_and_encoded_zone_id(*rv, {});
746 9x return *this;
747 }
748 // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
749 6x auto chars_left = endit - innerit;
750 10x if (chars_left >= 3 &&
751 4x *innerit++ == '%' &&
752 14x *innerit++ == '2' &&
753 4x *innerit++ == '5')
754 {
755 4x auto const nz = std::size_t(chars_left - 3);
756 4x core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
757 4x std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
758 4x pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
759 4x set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
760 4x return *this;
761 }
762 }
763 }
764 else
765 {
766 // IPvFuture
767 1x auto rv = grammar::parse(
768 1x s.substr(1, s.size() - 2),
769 detail::ipvfuture_rule);
770 1x if(rv)
771 1x return set_host_ipvfuture(rv->str);
772 }
773 }
774 201x else if(s.size() >= 7) // "0.0.0.0"
775 {
776 // IPv4-address
777 74x auto rv = parse_ipv4_address(s);
778 74x if(rv)
779 5x return set_host_ipv4(*rv);
780 }
781
782 // reg-name
783 206x op_t op(*this, &detail::ref(s));
784 206x auto const n = detail::re_encoded_size_unsafe(
785 s, detail::host_chars);
786 206x auto dest = set_host_impl(n, op);
787 206x impl_.decoded_[id_host] =
788 412x detail::to_size_type(detail::re_encode_unsafe(
789 dest,
790 206x impl_.get(id_path).data(),
791 s,
792 detail::host_chars));
793 206x BOOST_ASSERT(impl_.decoded_[id_host] ==
794 s.decoded_size());
795 206x impl_.host_type_ =
796 urls::host_type::name;
797 206x return *this;
798 206x }
799
800 inline
801 url_base&
802 13x url_base::
803 set_host_address(
804 core::string_view s)
805 {
806 13x if (!s.empty())
807 {
808 // IP-literal
809 12x if (s[0] != 'v')
810 {
811 // IPv6-address
812 11x auto innerit = s.begin();
813 11x auto endit = s.end();
814 11x auto rv = grammar::parse(
815 innerit,
816 endit,
817 ipv6_address_rule);
818 11x if(rv)
819 {
820 5x if (innerit == endit)
821 {
822 1x set_host_ipv6_and_encoded_zone_id(*rv, {});
823 5x return *this;
824 }
825 // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
826 4x auto chars_left = endit - innerit;
827 8x if (chars_left >= 2 &&
828 4x *innerit++ == '%')
829 {
830 4x core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
831 4x set_host_ipv6_and_zone_id(*rv, zone_id_str);
832 4x return *this;
833 }
834 }
835 }
836
837 // IPvFuture
838 7x auto rv = grammar::parse(s, detail::ipvfuture_rule);
839 7x if(rv)
840 1x return set_host_ipvfuture(rv->str);
841
842 6x if(s.size() >= 7) // "0.0.0.0"
843 {
844 // IPv4-address
845 5x auto rv2 = parse_ipv4_address(s);
846 5x if(rv2)
847 2x return set_host_ipv4(*rv2);
848 }
849 }
850
851 // reg-name
852 5x op_t op(*this, &s);
853 5x encoding_opts opt;
854 5x auto const n = encoded_size(
855 s, detail::host_chars, opt);
856 5x auto dest = set_host_impl(n, op);
857 5x encode(
858 dest,
859 5x impl_.get(id_path).data() - dest,
860 s,
861 detail::host_chars,
862 opt);
863 5x impl_.decoded_[id_host] =
864 5x detail::to_size_type(s.size());
865 5x impl_.host_type_ =
866 urls::host_type::name;
867 5x return *this;
868 5x }
869
870 inline
871 url_base&
872 11x url_base::
873 set_encoded_host_address(
874 pct_string_view s)
875 {
876 11x if( !s.empty() )
877 {
878 // IP-literal
879 10x if (s[0] != 'v')
880 {
881 // IPv6-address
882 9x auto innerit = s.begin();
883 9x auto endit = s.end();
884 9x auto rv = grammar::parse(
885 innerit,
886 endit,
887 ipv6_address_rule);
888 9x if(rv)
889 {
890 5x if (innerit == endit)
891 {
892 1x set_host_ipv6_and_encoded_zone_id(*rv, {});
893 6x return *this;
894 }
895 // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
896 4x auto chars_left = endit - innerit;
897 8x if (chars_left >= 3 &&
898 4x *innerit++ == '%' &&
899 12x *innerit++ == '2' &&
900 4x *innerit++ == '5')
901 {
902 4x auto const nz = std::size_t(chars_left - 3);
903 4x core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
904 4x std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
905 4x pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
906 4x set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
907 4x return *this;
908 }
909 }
910
911 4x if(s.size() >= 7) // "0.0.0.0"
912 {
913 // IPv4-address
914 3x auto rv2 = parse_ipv4_address(s);
915 3x if(rv2)
916 1x return set_host_ipv4(*rv2);
917 }
918 }
919 else
920 {
921 // IPvFuture
922 1x auto rv = grammar::parse(
923 s, detail::ipvfuture_rule);
924 1x if(rv)
925 1x return set_host_ipvfuture(rv->str);
926 }
927 }
928
929 // reg-name
930 4x op_t op(*this, &detail::ref(s));
931 4x auto const n = detail::re_encoded_size_unsafe(
932 s, detail::host_chars);
933 4x auto dest = set_host_impl(n, op);
934 4x impl_.decoded_[id_host] =
935 8x detail::to_size_type(detail::re_encode_unsafe(
936 dest,
937 4x impl_.get(id_path).data(),
938 s,
939 detail::host_chars));
940 4x BOOST_ASSERT(impl_.decoded_[id_host] ==
941 s.decoded_size());
942 4x impl_.host_type_ =
943 urls::host_type::name;
944 4x return *this;
945 4x }
946
947 inline
948 url_base&
949 203x url_base::
950 set_host_ipv4(
951 ipv4_address const& addr)
952 {
953 203x op_t op(*this);
954 char buf[urls::ipv4_address::max_str_len];
955 203x auto s = addr.to_buffer(buf, sizeof(buf));
956 203x auto dest = set_host_impl(s.size(), op);
957 203x std::memcpy(dest, s.data(), s.size());
958 203x impl_.decoded_[id_host] =
959 203x detail::to_size_type(impl_.len(id_host));
960 203x impl_.host_type_ = urls::host_type::ipv4;
961 203x auto bytes = addr.to_bytes();
962 203x std::memcpy(
963 203x impl_.ip_addr_,
964 203x bytes.data(),
965 bytes.size());
966 203x return *this;
967 203x }
968
969 inline
970 url_base&
971 5x url_base::
972 set_host_ipv6(
973 ipv6_address const& addr)
974 {
975 5x set_host_ipv6_and_encoded_zone_id(addr, encoded_zone_id());
976 5x return *this;
977 }
978
979 inline
980 url_base&
981 6x url_base::
982 set_zone_id(core::string_view s)
983 {
984 6x set_host_ipv6_and_zone_id(host_ipv6_address(), s);
985 6x return *this;
986 }
987
988 inline
989 url_base&
990 6x url_base::
991 set_encoded_zone_id(pct_string_view s)
992 {
993 6x set_host_ipv6_and_encoded_zone_id(host_ipv6_address(), s);
994 6x return *this;
995 }
996
997 inline
998 void
999 14x url_base::
1000 set_host_ipv6_and_zone_id(
1001 ipv6_address const& addr,
1002 core::string_view zone_id)
1003 {
1004 14x op_t op(*this, &zone_id);
1005 char ipv6_str_buf[urls::ipv6_address::max_str_len];
1006 14x auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
1007 14x bool const has_zone_id = !zone_id.empty();
1008 14x encoding_opts opt;
1009 14x auto const ipn = ipv6_str.size();
1010 14x auto const zn = encoded_size(zone_id, unreserved_chars, opt);
1011 14x auto const n = ipn + 2 + has_zone_id * (3 + zn);
1012 14x auto dest = set_host_impl(n, op);
1013 14x *dest++ = '[';
1014 14x std::memcpy(dest, ipv6_str.data(), ipn);
1015 14x dest += ipn;
1016 14x if (has_zone_id)
1017 {
1018 14x *dest++ = '%';
1019 14x *dest++ = '2';
1020 14x *dest++ = '5';
1021 14x encode(dest, zn, zone_id, unreserved_chars, opt);
1022 14x dest += zn;
1023 }
1024 14x *dest++ = ']';
1025 // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
1026 28x impl_.decoded_[id_host] = detail::to_size_type(
1027 14x ipn + 2 + has_zone_id * (1 + zone_id.size()));
1028 14x impl_.host_type_ = urls::host_type::ipv6;
1029 14x auto bytes = addr.to_bytes();
1030 14x std::memcpy(
1031 14x impl_.ip_addr_,
1032 14x bytes.data(),
1033 bytes.size());
1034 14x }
1035
1036 inline
1037 void
1038 73x url_base::
1039 set_host_ipv6_and_encoded_zone_id(
1040 ipv6_address const& addr,
1041 pct_string_view zone_id)
1042 {
1043 73x op_t op(*this, &detail::ref(zone_id));
1044 char ipv6_str_buf[urls::ipv6_address::max_str_len];
1045 73x auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
1046 73x bool const has_zone_id = !zone_id.empty();
1047 73x auto const ipn = ipv6_str.size();
1048 73x auto const zn = detail::re_encoded_size_unsafe(zone_id, unreserved_chars);
1049 73x auto const n = ipn + 2 + has_zone_id * (3 + zn);
1050 73x auto dest = set_host_impl(n, op);
1051 73x *dest++ = '[';
1052 73x std::memcpy(dest, ipv6_str.data(), ipn);
1053 73x dest += ipn;
1054 73x std::size_t dzn = 0;
1055 73x if (has_zone_id)
1056 {
1057 15x *dest++ = '%';
1058 15x *dest++ = '2';
1059 15x *dest++ = '5';
1060 15x dzn = detail::re_encode_unsafe(dest, dest + zn, zone_id, unreserved_chars);
1061 }
1062 73x *dest++ = ']';
1063 // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
1064 146x impl_.decoded_[id_host] = detail::to_size_type(
1065 73x ipn + 2 + has_zone_id * (1 + dzn));
1066 73x impl_.host_type_ = urls::host_type::ipv6;
1067 73x auto bytes = addr.to_bytes();
1068 73x std::memcpy(
1069 73x impl_.ip_addr_,
1070 73x bytes.data(),
1071 bytes.size());
1072 73x }
1073
1074 inline
1075 url_base&
1076 7x url_base::
1077 set_host_ipvfuture(
1078 core::string_view s)
1079 {
1080 7x op_t op(*this, &s);
1081 // validate
1082 8x grammar::parse(s,
1083 detail::ipvfuture_rule
1084 8x ).value(BOOST_URL_POS);
1085 6x auto dest = set_host_impl(
1086 6x s.size() + 2, op);
1087 6x *dest++ = '[';
1088 6x dest += s.copy(dest, s.size());
1089 6x *dest = ']';
1090 6x impl_.host_type_ =
1091 urls::host_type::ipvfuture;
1092 6x impl_.decoded_[id_host] =
1093 6x detail::to_size_type(s.size() + 2);
1094 6x return *this;
1095 7x }
1096
1097 inline
1098 url_base&
1099 4x url_base::
1100 set_host_name(
1101 core::string_view s)
1102 {
1103 4x bool is_ipv4 = false;
1104 4x if(s.size() >= 7) // "0.0.0.0"
1105 {
1106 // IPv4-address
1107 3x if(parse_ipv4_address(s).has_value())
1108 1x is_ipv4 = true;
1109 }
1110 4x auto allowed = detail::host_chars;
1111 4x if(is_ipv4)
1112 1x allowed = allowed - '.';
1113
1114 4x op_t op(*this, &s);
1115 4x encoding_opts opt;
1116 4x auto const n = encoded_size(
1117 s, allowed, opt);
1118 4x auto dest = set_host_impl(n, op);
1119 4x encode_unsafe(
1120 dest,
1121 n,
1122 s,
1123 allowed,
1124 opt);
1125 4x impl_.host_type_ =
1126 urls::host_type::name;
1127 4x impl_.decoded_[id_host] =
1128 4x detail::to_size_type(s.size());
1129 4x return *this;
1130 4x }
1131
1132 inline
1133 url_base&
1134 4x url_base::
1135 set_encoded_host_name(
1136 pct_string_view s)
1137 {
1138 4x bool is_ipv4 = false;
1139 4x if(s.size() >= 7) // "0.0.0.0"
1140 {
1141 // IPv4-address
1142 3x if(parse_ipv4_address(s).has_value())
1143 1x is_ipv4 = true;
1144 }
1145 4x auto allowed = detail::host_chars;
1146 4x if(is_ipv4)
1147 1x allowed = allowed - '.';
1148
1149 4x op_t op(*this, &detail::ref(s));
1150 4x auto const n = detail::re_encoded_size_unsafe(
1151 s, allowed);
1152 4x auto dest = set_host_impl(n, op);
1153 4x impl_.decoded_[id_host] =
1154 8x detail::to_size_type(detail::re_encode_unsafe(
1155 dest,
1156 4x dest + n,
1157 s,
1158 allowed));
1159 4x BOOST_ASSERT(
1160 impl_.decoded_[id_host] ==
1161 s.decoded_size());
1162 4x impl_.host_type_ =
1163 urls::host_type::name;
1164 4x return *this;
1165 4x }
1166
1167 //------------------------------------------------
1168
1169 inline
1170 url_base&
1171 215x url_base::
1172 set_port_number(
1173 std::uint16_t n)
1174 {
1175 215x op_t op(*this);
1176 auto s =
1177 215x detail::make_printed(n);
1178 215x auto dest = set_port_impl(
1179 215x s.string().size(), op);
1180 215x std::memcpy(
1181 215x dest, s.string().data(),
1182 215x s.string().size());
1183 215x impl_.port_number_ = n;
1184 215x return *this;
1185 215x }
1186
1187 inline
1188 url_base&
1189 389x url_base::
1190 set_port(
1191 core::string_view s)
1192 {
1193 389x op_t op(*this, &s);
1194 410x auto t = grammar::parse(s,
1195 21x detail::port_rule{}
1196 389x ).value(BOOST_URL_POS);
1197 auto dest =
1198 368x set_port_impl(t.str.size(), op);
1199 368x std::memcpy(dest,
1200 368x t.str.data(), t.str.size());
1201 368x if(t.has_number)
1202 208x impl_.port_number_ = t.number;
1203 else
1204 160x impl_.port_number_ = 0;
1205 368x return *this;
1206 389x }
1207
1208 inline
1209 url_base&
1210 206x url_base::
1211 remove_port() noexcept
1212 {
1213 206x op_t op(*this);
1214 206x resize_impl(id_port, 0, op);
1215 206x impl_.port_number_ = 0;
1216 412x return *this;
1217 206x }
1218
1219 //------------------------------------------------
1220 //
1221 // Compound Fields
1222 //
1223 //------------------------------------------------
1224
1225 inline
1226 url_base&
1227 14x url_base::
1228 remove_origin()
1229 {
1230 // these two calls perform 2 memmoves instead of 1
1231 14x remove_authority();
1232 14x remove_scheme();
1233 14x return *this;
1234 }
1235
1236 //------------------------------------------------
1237 //
1238 // Path
1239 //
1240 //------------------------------------------------
1241
1242 inline
1243 bool
1244 52x url_base::
1245 set_path_absolute(
1246 bool absolute)
1247 {
1248 52x op_t op(*this);
1249
1250 // check if path empty
1251 52x if(impl_.len(id_path) == 0)
1252 {
1253 40x if(! absolute)
1254 {
1255 // already not absolute
1256 34x return true;
1257 }
1258
1259 // add '/'
1260 6x auto dest = resize_impl(
1261 id_path, 1, op);
1262 6x *dest = '/';
1263 6x ++impl_.decoded_[id_path];
1264 6x return true;
1265 }
1266
1267 // check if path absolute
1268 12x if(s_[impl_.offset(id_path)] == '/')
1269 {
1270 9x if(absolute)
1271 {
1272 // already absolute
1273 2x return true;
1274 }
1275
1276 11x if( has_authority() &&
1277 4x impl_.len(id_path) > 1)
1278 {
1279 // can't do it, paths are always
1280 // absolute when authority present!
1281 2x return false;
1282 }
1283
1284 5x auto p = encoded_path();
1285 5x auto pos = p.find_first_of(":/", 1);
1286 6x if (pos != core::string_view::npos &&
1287 1x p[pos] == ':')
1288 {
1289 // prepend with .
1290 1x auto n = impl_.len(id_path);
1291 1x resize_impl(id_path, n + 1, op);
1292 1x std::memmove(
1293 2x s_ + impl_.offset(id_path) + 1,
1294 1x s_ + impl_.offset(id_path), n);
1295 1x *(s_ + impl_.offset(id_path)) = '.';
1296 1x ++impl_.decoded_[id_path];
1297 1x return true;
1298 }
1299
1300 // remove '/'
1301 4x auto n = impl_.len(id_port);
1302 4x impl_.split(id_port, n + 1);
1303 4x resize_impl(id_port, n, op);
1304 4x --impl_.decoded_[id_path];
1305 4x return true;
1306 }
1307
1308 3x if(! absolute)
1309 {
1310 // already not absolute
1311 1x return true;
1312 }
1313
1314 // add '/'
1315 2x auto n = impl_.len(id_port);
1316 2x auto dest = resize_impl(
1317 2x id_port, n + 1, op) + n;
1318 2x impl_.split(id_port, n);
1319 2x *dest = '/';
1320 2x ++impl_.decoded_[id_path];
1321 2x return true;
1322 52x }
1323
1324 inline
1325 url_base&
1326 298x url_base::
1327 set_path(
1328 core::string_view s)
1329 {
1330 298x op_t op(*this, &s);
1331 298x encoding_opts opt;
1332
1333 //------------------------------------------------
1334 //
1335 // Calculate encoded size
1336 //
1337 // - "/"s are not encoded
1338 // - "%2F"s are not encoded
1339 //
1340 // - reserved path chars are re-encoded
1341 // - colons in first segment might need to be re-encoded
1342 // - the path might need to receive a prefix
1343 298x auto const n = encoded_size(
1344 s, detail::path_chars, opt);
1345 298x std::size_t n_reencode_colons = 0;
1346 298x core::string_view first_seg;
1347 298x if (!has_scheme() &&
1348 337x !has_authority() &&
1349 39x !s.starts_with('/'))
1350 {
1351 // the first segment with unencoded colons would look
1352 // like the scheme
1353 6x first_seg = detail::to_sv(s);
1354 6x std::size_t p = s.find('/');
1355 6x if (p != core::string_view::npos)
1356 2x first_seg = s.substr(0, p);
1357 6x n_reencode_colons = std::count(
1358 12x first_seg.begin(), first_seg.end(), ':');
1359 }
1360 // the authority can only be followed by an empty or relative path
1361 // if we have an authority and the path is a non-empty relative path, we
1362 // add the "/" prefix to make it valid.
1363 bool make_absolute =
1364 298x has_authority() &&
1365 305x !s.starts_with('/') &&
1366 7x !s.empty();
1367 // a path starting with "//" might look like the authority.
1368 // we add a "/." prefix to prevent that
1369 bool add_dot_segment =
1370 592x !make_absolute &&
1371 294x s.starts_with("//");
1372
1373 //------------------------------------------------
1374 //
1375 // Re-encode data
1376 //
1377 596x auto dest = set_path_impl(
1378 298x n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1379 298x impl_.decoded_[id_path] = 0;
1380 298x if (!dest)
1381 {
1382 3x impl_.nseg_ = 0;
1383 3x return *this;
1384 }
1385 295x if (make_absolute)
1386 {
1387 4x *dest++ = '/';
1388 4x impl_.decoded_[id_path] += 1;
1389 }
1390 291x else if (add_dot_segment)
1391 {
1392 12x *dest++ = '/';
1393 12x *dest++ = '.';
1394 12x impl_.decoded_[id_path] += 2;
1395 }
1396 590x dest += encode_unsafe(
1397 dest,
1398 295x impl_.get(id_query).data() - dest,
1399 first_seg,
1400 295x detail::segment_chars - ':',
1401 opt);
1402 295x dest += encode_unsafe(
1403 dest,
1404 295x impl_.get(id_query).data() - dest,
1405 s.substr(first_seg.size()),
1406 detail::path_chars,
1407 opt);
1408 295x impl_.decoded_[id_path] +=
1409 295x detail::to_size_type(s.size());
1410 295x BOOST_ASSERT(!dest || dest == impl_.get(id_query).data());
1411 295x BOOST_ASSERT(
1412 impl_.decoded_[id_path] ==
1413 s.size() + make_absolute + 2 * add_dot_segment);
1414
1415 //------------------------------------------------
1416 //
1417 // Update path parameters
1418 //
1419 // get the encoded_path with the replacements we applied
1420 295x if (s == "/")
1421 {
1422 // "/" maps to sequence {}
1423 144x impl_.nseg_ = 0;
1424 }
1425 151x else if (!s.empty())
1426 {
1427 146x if (s.starts_with("/./"))
1428 1x s = s.substr(2);
1429 // count segments as number of '/'s + 1
1430 292x impl_.nseg_ = detail::to_size_type(
1431 146x std::count(
1432 292x s.begin() + 1, s.end(), '/') + 1);
1433 }
1434 else
1435 {
1436 // an empty relative path maps to sequence {}
1437 5x impl_.nseg_ = 0;
1438 }
1439
1440 295x check_invariants();
1441 295x return *this;
1442 298x }
1443
1444 inline
1445 url_base&
1446 300x url_base::
1447 set_encoded_path(
1448 pct_string_view s)
1449 {
1450 300x op_t op(*this, &detail::ref(s));
1451
1452 //------------------------------------------------
1453 //
1454 // Calculate re-encoded output size
1455 //
1456 // - reserved path chars are re-encoded
1457 // - colons in first segment might need to be re-encoded
1458 // - the path might need to receive a prefix
1459 300x auto const n = detail::re_encoded_size_unsafe(
1460 s, detail::path_chars);
1461 300x std::size_t n_reencode_colons = 0;
1462 300x core::string_view first_seg;
1463 300x if (!has_scheme() &&
1464 333x !has_authority() &&
1465 33x !s.starts_with('/'))
1466 {
1467 // the first segment with unencoded colons would look
1468 // like the scheme
1469 26x first_seg = detail::to_sv(s);
1470 26x std::size_t p = s.find('/');
1471 26x if (p != core::string_view::npos)
1472 9x first_seg = s.substr(0, p);
1473 26x n_reencode_colons = std::count(
1474 52x first_seg.begin(), first_seg.end(), ':');
1475 }
1476 // the authority can only be followed by an empty or relative path
1477 // if we have an authority and the path is a non-empty relative path, we
1478 // add the "/" prefix to make it valid.
1479 bool make_absolute =
1480 300x has_authority() &&
1481 444x !s.starts_with('/') &&
1482 144x !s.empty();
1483 // a path starting with "//" might look like the authority
1484 // we add a "/." prefix to prevent that
1485 bool add_dot_segment =
1486 504x !make_absolute &&
1487 363x !has_authority() &&
1488 63x s.starts_with("//");
1489
1490 //------------------------------------------------
1491 //
1492 // Re-encode data
1493 //
1494 600x auto dest = set_path_impl(
1495 300x n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1496 300x impl_.decoded_[id_path] = 0;
1497 300x if (!dest)
1498 {
1499 2x impl_.nseg_ = 0;
1500 2x return *this;
1501 }
1502 298x if (make_absolute)
1503 {
1504 96x *dest++ = '/';
1505 96x impl_.decoded_[id_path] += 1;
1506 }
1507 202x else if (add_dot_segment)
1508 {
1509 5x *dest++ = '/';
1510 5x *dest++ = '.';
1511 5x impl_.decoded_[id_path] += 2;
1512 }
1513 298x impl_.decoded_[id_path] +=
1514 298x detail::to_size_type(detail::re_encode_unsafe(
1515 dest,
1516 298x impl_.get(id_query).data(),
1517 first_seg,
1518 298x detail::segment_chars - ':'));
1519 298x impl_.decoded_[id_path] +=
1520 596x detail::to_size_type(detail::re_encode_unsafe(
1521 dest,
1522 298x impl_.get(id_query).data(),
1523 s.substr(first_seg.size()),
1524 detail::path_chars));
1525 298x BOOST_ASSERT(dest == impl_.get(id_query).data());
1526 298x BOOST_ASSERT(
1527 impl_.decoded_[id_path] ==
1528 s.decoded_size() + make_absolute + 2 * add_dot_segment);
1529
1530 //------------------------------------------------
1531 //
1532 // Update path parameters
1533 //
1534 // get the encoded_path with the replacements we applied
1535 298x if (s == "/")
1536 {
1537 // "/" maps to sequence {}
1538 20x impl_.nseg_ = 0;
1539 }
1540 278x else if (!s.empty())
1541 {
1542 227x if (s.starts_with("/./"))
1543 7x s = s.substr(2);
1544 // count segments as number of '/'s + 1
1545 454x impl_.nseg_ = detail::to_size_type(
1546 227x std::count(
1547 454x s.begin() + 1, s.end(), '/') + 1);
1548 }
1549 else
1550 {
1551 // an empty relative path maps to sequence {}
1552 51x impl_.nseg_ = 0;
1553 }
1554
1555 298x check_invariants();
1556 298x return *this;
1557 300x }
1558
1559 inline
1560 segments_ref
1561 1146x url_base::
1562 segments() noexcept
1563 {
1564 1146x return {*this};
1565 }
1566
1567 inline
1568 segments_encoded_ref
1569 522x url_base::
1570 encoded_segments() noexcept
1571 {
1572 522x return {*this};
1573 }
1574
1575 //------------------------------------------------
1576 //
1577 // Query
1578 //
1579 //------------------------------------------------
1580
1581 inline
1582 url_base&
1583 169x url_base::
1584 set_query(
1585 core::string_view s)
1586 {
1587 169x edit_params(
1588 169x detail::params_iter_impl(impl_),
1589 169x detail::params_iter_impl(impl_, 0),
1590 338x detail::query_string_iter(s, true));
1591 169x return *this;
1592 }
1593
1594 inline
1595 url_base&
1596 179x url_base::
1597 set_encoded_query(
1598 pct_string_view s)
1599 {
1600 179x op_t op(*this);
1601 179x std::size_t n = 0; // encoded size
1602 179x std::size_t nparam = 1; // param count
1603 179x auto const end = s.end();
1604 179x auto p = s.begin();
1605
1606 // measure
1607 742x while(p != end)
1608 {
1609 563x if(*p == '&')
1610 {
1611 4x ++p;
1612 4x ++n;
1613 4x ++nparam;
1614 }
1615 559x else if(*p != '%')
1616 {
1617 533x if(detail::query_chars(*p))
1618 518x n += 1; // allowed
1619 else
1620 15x n += 3; // escaped
1621 533x ++p;
1622 }
1623 else
1624 {
1625 // escape
1626 26x n += 3;
1627 26x p += 3;
1628 }
1629 }
1630
1631 // resize
1632 179x auto dest = resize_impl(
1633 179x id_query, n + 1, op);
1634 179x *dest++ = '?';
1635
1636 // encode
1637 179x impl_.decoded_[id_query] =
1638 358x detail::to_size_type(detail::re_encode_unsafe(
1639 dest,
1640 179x dest + n,
1641 s,
1642 detail::query_chars));
1643 179x BOOST_ASSERT(
1644 impl_.decoded_[id_query] ==
1645 s.decoded_size());
1646 179x impl_.nparam_ =
1647 179x detail::to_size_type(nparam);
1648 179x return *this;
1649 179x }
1650
1651 inline
1652 params_ref
1653 986x url_base::
1654 params() noexcept
1655 {
1656 return params_ref(
1657 *this,
1658 encoding_opts{
1659 986x true, false, false});
1660 }
1661
1662 inline
1663 params_ref
1664 4x url_base::
1665 params(encoding_opts opt) noexcept
1666 {
1667 4x return {*this, opt};
1668 }
1669
1670 inline
1671 params_encoded_ref
1672 77x url_base::
1673 encoded_params() noexcept
1674 {
1675 77x return {*this};
1676 }
1677
1678 inline
1679 url_base&
1680 1x url_base::
1681 set_params(
1682 std::initializer_list<param_view> ps,
1683 encoding_opts opts) noexcept
1684 {
1685 1x params(opts).assign(ps);
1686 1x return *this;
1687 }
1688
1689 inline
1690 url_base&
1691 1x url_base::
1692 set_encoded_params( std::initializer_list< param_pct_view > ps ) noexcept
1693 {
1694 1x encoded_params().assign(ps);
1695 1x return *this;
1696 }
1697
1698 inline
1699 url_base&
1700 991x url_base::
1701 remove_query() noexcept
1702 {
1703 991x op_t op(*this);
1704 991x resize_impl(id_query, 0, op);
1705 991x impl_.nparam_ = 0;
1706 991x impl_.decoded_[id_query] = 0;
1707 1982x return *this;
1708 991x }
1709
1710 //------------------------------------------------
1711 //
1712 // Fragment
1713 //
1714 //------------------------------------------------
1715
1716 inline
1717 url_base&
1718 408x url_base::
1719 remove_fragment() noexcept
1720 {
1721 408x op_t op(*this);
1722 408x resize_impl(id_frag, 0, op);
1723 408x impl_.decoded_[id_frag] = 0;
1724 816x return *this;
1725 408x }
1726
1727 inline
1728 url_base&
1729 151x url_base::
1730 set_fragment(core::string_view s)
1731 {
1732 151x op_t op(*this, &s);
1733 151x encoding_opts opt;
1734 151x auto const n = encoded_size(
1735 s,
1736 detail::fragment_chars,
1737 opt);
1738 151x auto dest = resize_impl(
1739 id_frag, n + 1, op);
1740 151x *dest++ = '#';
1741 151x encode_unsafe(
1742 dest,
1743 n,
1744 s,
1745 detail::fragment_chars,
1746 opt);
1747 151x impl_.decoded_[id_frag] =
1748 151x detail::to_size_type(s.size());
1749 151x return *this;
1750 151x }
1751
1752 inline
1753 url_base&
1754 183x url_base::
1755 set_encoded_fragment(
1756 pct_string_view s)
1757 {
1758 183x op_t op(*this, &detail::ref(s));
1759 auto const n =
1760 183x detail::re_encoded_size_unsafe(
1761 s,
1762 detail::fragment_chars);
1763 183x auto dest = resize_impl(
1764 183x id_frag, n + 1, op);
1765 183x *dest++ = '#';
1766 183x impl_.decoded_[id_frag] =
1767 366x detail::to_size_type(detail::re_encode_unsafe(
1768 dest,
1769 183x dest + n,
1770 s,
1771 detail::fragment_chars));
1772 183x BOOST_ASSERT(
1773 impl_.decoded_[id_frag] ==
1774 s.decoded_size());
1775 183x return *this;
1776 183x }
1777
1778 //------------------------------------------------
1779 //
1780 // Resolution
1781 //
1782 //------------------------------------------------
1783
1784 inline
1785 system::result<void>
1786 517x url_base::
1787 resolve(
1788 url_view_base const& ref)
1789 {
1790 517x if(! has_scheme())
1791 {
1792 2x BOOST_URL_RETURN_EC(error::not_a_base);
1793 }
1794
1795 515x if (this == &ref)
1796 {
1797 2x normalize_path();
1798 2x return {};
1799 }
1800
1801 513x op_t op(*this);
1802
1803 //
1804 // 5.2.2. Transform References
1805 // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2
1806 //
1807
1808 783x if( ref.has_scheme() &&
1809 270x ref.scheme() != scheme())
1810 {
1811 202x reserve_impl(ref.size(), op);
1812 202x copy(ref);
1813 202x normalize_path();
1814 202x return {};
1815 }
1816 311x if(ref.has_authority())
1817 {
1818 79x reserve_impl(
1819 79x impl_.offset(id_user) + ref.size(), op);
1820 79x set_encoded_authority(
1821 ref.encoded_authority());
1822 79x set_encoded_path(
1823 ref.encoded_path());
1824 79x if (ref.encoded_path().empty())
1825 33x set_path_absolute(false);
1826 else
1827 46x normalize_path();
1828 79x if(ref.has_query())
1829 8x set_encoded_query(
1830 ref.encoded_query());
1831 else
1832 71x remove_query();
1833 79x if(ref.has_fragment())
1834 7x set_encoded_fragment(
1835 ref.encoded_fragment());
1836 else
1837 72x remove_fragment();
1838 79x return {};
1839 }
1840 232x if(ref.encoded_path().empty())
1841 {
1842 43x reserve_impl(
1843 43x impl_.offset(id_query) +
1844 43x ref.size(), op);
1845 43x normalize_path();
1846 43x if(ref.has_query())
1847 {
1848 12x set_encoded_query(
1849 ref.encoded_query());
1850 }
1851 43x if(ref.has_fragment())
1852 20x set_encoded_fragment(
1853 ref.encoded_fragment());
1854 else
1855 23x remove_fragment();
1856 43x return {};
1857 }
1858 189x if(ref.is_path_absolute())
1859 {
1860 41x reserve_impl(
1861 41x impl_.offset(id_path) +
1862 41x ref.size(), op);
1863 41x set_encoded_path(
1864 ref.encoded_path());
1865 41x normalize_path();
1866 41x if(ref.has_query())
1867 4x set_encoded_query(
1868 ref.encoded_query());
1869 else
1870 37x remove_query();
1871 41x if(ref.has_fragment())
1872 2x set_encoded_fragment(
1873 ref.encoded_fragment());
1874 else
1875 39x remove_fragment();
1876 41x return {};
1877 }
1878 // General case: ref is relative path
1879 148x reserve_impl(
1880 148x impl_.offset(id_query) +
1881 148x ref.size(), op);
1882 // 5.2.3. Merge Paths
1883 148x auto es = encoded_segments();
1884 148x if(es.size() > 0)
1885 {
1886 141x es.pop_back();
1887 }
1888 296x es.insert(es.end(),
1889 148x ref.encoded_segments().begin(),
1890 148x ref.encoded_segments().end());
1891 148x normalize_path();
1892 148x if(ref.has_query())
1893 10x set_encoded_query(
1894 ref.encoded_query());
1895 else
1896 138x remove_query();
1897 148x if(ref.has_fragment())
1898 13x set_encoded_fragment(
1899 ref.encoded_fragment());
1900 else
1901 135x remove_fragment();
1902 148x return {};
1903 513x }
1904
1905 //------------------------------------------------
1906 //
1907 // Normalization
1908 //
1909 //------------------------------------------------
1910
1911 template <
1912 class AllowedCharset,
1913 class IgnoredCharset>
1914 void
1915 3211x url_base::
1916 normalize_octets_impl(
1917 int id,
1918 AllowedCharset const& allowed,
1919 IgnoredCharset const& ignored,
1920 op_t& op) noexcept
1921 {
1922 3211x char* it = s_ + impl_.offset(id);
1923 3211x char* end = s_ + impl_.offset(id + 1);
1924 3211x char d = 0;
1925 3211x char* dest = it;
1926 16962x while (it < end)
1927 {
1928 13751x if (*it != '%')
1929 {
1930 13511x *dest = *it;
1931 13511x ++it;
1932 13511x ++dest;
1933 13511x continue;
1934 }
1935 240x BOOST_ASSERT(end - it >= 3);
1936
1937 // decode unreserved octets
1938 240x d = detail::decode_one(it + 1);
1939 322x if (allowed(d) &&
1940 82x !ignored(d))
1941 {
1942 82x *dest = d;
1943 82x it += 3;
1944 82x ++dest;
1945 82x continue;
1946 }
1947
1948 // uppercase percent-encoding triplets
1949 158x *dest++ = '%';
1950 158x ++it;
1951 158x *dest++ = grammar::to_upper(*it++);
1952 158x *dest++ = grammar::to_upper(*it++);
1953 }
1954 3211x if (it != dest)
1955 {
1956 32x auto diff = it - dest;
1957 32x auto n = impl_.len(id) - diff;
1958 32x shrink_impl(id, n, op);
1959 32x s_[size()] = '\0';
1960 }
1961 3211x }
1962
1963 template<class CharSet>
1964 void
1965 3211x url_base::
1966 normalize_octets_impl(
1967 int idx,
1968 CharSet const& allowed,
1969 op_t& op) noexcept
1970 {
1971 3211x return normalize_octets_impl(
1972 3211x idx, allowed, detail::empty_chars, op);
1973 }
1974
1975 inline
1976 url_base&
1977 263x url_base::
1978 normalize_scheme()
1979 {
1980 263x to_lower_impl(id_scheme);
1981 263x return *this;
1982 }
1983
1984 inline
1985 url_base&
1986 609x url_base::
1987 normalize_authority()
1988 {
1989 609x op_t op(*this);
1990
1991 // normalize host
1992 609x if (host_type() == urls::host_type::name)
1993 {
1994 356x normalize_octets_impl(
1995 id_host,
1996 detail::reg_name_chars, op);
1997 }
1998 609x decoded_to_lower_impl(id_host);
1999
2000 // normalize password
2001 609x normalize_octets_impl(id_pass, detail::password_chars, op);
2002
2003 // normalize user
2004 609x normalize_octets_impl(id_user, detail::user_chars, op);
2005 1218x return *this;
2006 609x }
2007
2008 inline
2009 url_base&
2010 1112x url_base::
2011 normalize_path()
2012 {
2013 1112x op_t op(*this);
2014
2015 //
2016 // Step 1: Percent-encoding normalization
2017 //
2018 // Decode percent-encoded unreserved characters,
2019 // and uppercase remaining hex digits.
2020 //
2021 // This can introduce new '.' from %2E,
2022 // but reserved characters stay encoded.
2023 //
2024 // These new '.' characters can create
2025 // ambiguities that Steps 2 and 3 must handle.
2026 //
2027 1112x normalize_octets_impl(id_path, unreserved_chars, op);
2028 1112x core::string_view p = impl_.get(id_path);
2029 1112x char* p_dest = s_ + impl_.offset(id_path);
2030 1112x char* p_end = s_ + impl_.offset(id_path + 1);
2031 1112x auto pn = p.size();
2032
2033 //
2034 // Step 2: Preserve existing path shields
2035 //
2036 // If the URL has no authority, a path starting
2037 // with "//" would be misinterpreted as an
2038 // authority when serialized and re-parsed.
2039 // Some paths already have a dot prefix ("/."
2040 // or "./") that shields against this. We
2041 // preserve them so remove_dot_segments (Step 3)
2042 // does not strip them. Step 1 can also reveal
2043 // new shields by decoding %2E to '.'.
2044 //
2045 // This is an optimization. Step 4 would create
2046 // a new shield anyway if remove_dot_segments
2047 // produces a problematic output, but preserving
2048 // an existing shield avoids the memmove in
2049 // Step 4.
2050 //
2051 // path_shield_len: number of leading bytes
2052 // to skip during remove_dot_segments.
2053 // Always 0 (no shield) or 2 ("/." or "./").
2054 //
2055 1112x auto path_shield_len = 0;
2056 1112x if (!has_authority())
2057 {
2058 354x if (p.starts_with("/./"))
2059 {
2060 // (a) Absolute path with "/." prefix.
2061 //
2062 // Check if "/." shields a "//"
2063 // underneath (possibly through
2064 // multiple "/./" layers like "/././/").
2065 // If so, preserve the first 2 chars.
2066 //
2067 // /.//evil -> /.//evil (preserved)
2068 // Without it: //evil (ambiguous).
2069 // /././/evil -> /.//evil (same idea)
2070 //
2071 // Requires no authority (with authority,
2072 // "//" in the path is unambiguous):
2073 // http://h/.//x -> http://h//x (OK)
2074 // Scheme is irrelevant (absolute paths
2075 // can't be confused with a scheme):
2076 // scheme:/.//x and /.//x both need it.
2077 //
2078 25x path_shield_len = 2;
2079 27x while (p.substr(path_shield_len, 3).starts_with("/./"))
2080 {
2081 2x path_shield_len += 2;
2082 }
2083 25x if (p.substr(path_shield_len).starts_with("//"))
2084 {
2085 17x path_shield_len = 2;
2086 }
2087 else
2088 {
2089 8x path_shield_len = 0;
2090 }
2091 }
2092 329x else if (
2093 417x !has_scheme() &&
2094 88x p.starts_with("./"))
2095 {
2096 // (b) Relative path with "./" prefix,
2097 // no scheme.
2098 //
2099 // Check if "./" shields a "//"
2100 // underneath. If so, preserve it.
2101 //
2102 // .//evil -> .//evil (preserved)
2103 // Without it: //evil (ambiguous).
2104 // ././/evil -> .//evil (same idea)
2105 //
2106 // Requires no authority (with authority,
2107 // "//" in the path is unambiguous):
2108 // //h/.//x -> //h//x (OK)
2109 // Requires no scheme: with a scheme,
2110 // remove_dot_segments would strip "./"
2111 // and Step 4 handles any resulting "//".
2112 //
2113 21x path_shield_len = 1;
2114 26x while (p.substr(path_shield_len, 3).starts_with("/./"))
2115 {
2116 5x path_shield_len += 2;
2117 }
2118 21x if (p.substr(path_shield_len).starts_with("//"))
2119 {
2120 13x path_shield_len = 2;
2121 }
2122 else
2123 {
2124 8x path_shield_len = 0;
2125 }
2126 }
2127 }
2128
2129 //
2130 // Step 3: Remove "." and ".." segments
2131 //
2132 // RFC 3986 Section 5.2.4.
2133 //
2134 // If path_shield_len > 0, the first
2135 // path_shield_len characters are an existing
2136 // path shield ("/." or "./") that must
2137 // be preserved (see Step 2), we tell
2138 // remove_dot_segments to start after that
2139 // prefix.
2140 //
2141 // Note: remove_dot_segments only recognizes
2142 // literal '.' and '..', not encoded forms like
2143 // '%2E'. This is correct here because Step 1
2144 // already decoded %2E to '.'. If this function
2145 // were called without Step 1, encoded dots
2146 // would be missed.
2147 //
2148 1112x bool was_absolute = is_path_absolute();
2149 1112x p.remove_prefix(path_shield_len);
2150 1112x p_dest += path_shield_len;
2151 1112x auto n = detail::remove_dot_segments(
2152 1112x p_dest, p_end, p);
2153
2154 //
2155 // Step 4: Create path shield if needed,
2156 // then shrink path to final size
2157 //
2158 // remove_dot_segments can produce output that
2159 // needs a 2-byte shield prefix, as explained
2160 // in step 2. The memmove below writes within
2161 // the path region; when ".." cancellation has
2162 // consumed >= 2 bytes there is already slack,
2163 // but for short inputs that take the shield
2164 // branch without any cancellation (e.g. "//"),
2165 // the region has to grow to fit the prefix.
2166 //
2167 3336x bool needs_shield = [&]()
2168 {
2169 1112x if (path_shield_len)
2170 {
2171 // Step 2 already preserved a shield.
2172 30x return false;
2173 }
2174 1082x if (has_authority())
2175 {
2176 // With an authority, "//" in the path
2177 // is unambiguous and the path is always
2178 // absolute (path-abempty).
2179 758x return false;
2180 }
2181 324x if (n == 0)
2182 {
2183 // Empty output. Nothing to shield.
2184 29x return false;
2185 }
2186 295x if (p_dest[0] != '/')
2187 {
2188 // Output doesn't start with '/'.
2189 // No authority ambiguity, and if it
2190 // was relative, it stayed relative.
2191 129x return false;
2192 }
2193 166x if (n >= 2 && p_dest[1] == '/')
2194 {
2195 // Output starts with "//": would be
2196 // misinterpreted as authority.
2197 // /a/..//evil -> //evil
2198 // so we need a shield
2199 // /a/..//evil -> /.//evil
2200 11x return true;
2201 }
2202 155x if (!was_absolute)
2203 {
2204 // Relative path became absolute: ".."
2205 // canceled all leading segments and
2206 // the remaining input started with "/"
2207 // because of how remove_dot_segments is
2208 // defined.
2209 // a/..//evil -> /evil -> .//evil
2210 // a/b/../..//evil -> /evil -> .//evil
2211 14x return true;
2212 }
2213 141x return false;
2214 1112x }();
2215 1112x if (needs_shield)
2216 {
2217 25x if (n + 2 > pn)
2218 {
2219 // No ".. cancellation slack — grow the path
2220 // region to fit the 2-byte shield. p_dest may
2221 // be invalidated by the underlying reallocation.
2222 3x p_dest = resize_impl(id_path, n + 2, op);
2223 3x pn = n + 2;
2224 }
2225 25x std::memmove(p_dest + 2, p_dest, n);
2226 25x if (was_absolute)
2227 {
2228 // "/." keeps the path absolute.
2229 11x p_dest[0] = '/';
2230 11x p_dest[1] = '.';
2231 }
2232 else
2233 {
2234 // "./" keeps the path relative.
2235 14x p_dest[0] = '.';
2236 14x p_dest[1] = '/';
2237 }
2238 25x path_shield_len = 2;
2239 }
2240 1112x if (n != pn)
2241 {
2242 200x BOOST_ASSERT(n < pn);
2243 200x shrink_impl(id_path, n + path_shield_len, op);
2244 }
2245
2246 //
2247 // Step 5: Encode colons in first segment
2248 //
2249 // If the URL has no scheme and no authority,
2250 // a colon in the first path segment would be
2251 // misinterpreted as a scheme delimiter when
2252 // serialized and re-parsed.
2253 //
2254 // Colons can appear in the first segment because
2255 // remove_dot_segments (Step 3) canceled preceding
2256 // segments via ".." and exposed a colon that was
2257 // deeper in the path.
2258 //
2259 // Example (exposed by dot removal):
2260 // a/../b:c -> Step 3: b:c
2261 // -> Step 5: b%3Ac
2262 //
2263 // ALL colons in the first segment must be
2264 // encoded, not just the first one. In a
2265 // schemeless relative-reference, the first
2266 // segment must be segment-nz-nc (RFC 3986
2267 // Section 4.2), which does not allow ':':
2268 // path-noscheme = segment-nz-nc *( "/" segment )
2269 // segment-nz-nc = 1*( unreserved / pct-encoded
2270 // / sub-delims / "@" )
2271 // So "b%3Ac:d" would fail to re-parse.
2272 // All colons must go: "b%3Ac%3Ad".
2273 //
2274 // Requires no scheme (with a scheme, colons
2275 // in the path are unambiguous):
2276 // scheme:a:b -> scheme:a:b (OK)
2277 // Requires no authority (with authority,
2278 // path starts with "/" so the first segment
2279 // is empty, no colon issue).
2280 //
2281 1230x if (!has_scheme() &&
2282 118x !has_authority())
2283 {
2284 99x p = impl_.get(id_path);
2285 99x core::string_view first_seg = p;
2286 99x auto i = first_seg.find('/');
2287 99x if (i != core::string_view::npos)
2288 {
2289 68x first_seg = p.substr(0, i);
2290 }
2291 99x if (first_seg.contains(':'))
2292 {
2293 12x pn = p.size();
2294 auto cn =
2295 12x std::count(
2296 first_seg.begin(),
2297 first_seg.end(),
2298 12x ':');
2299 12x resize_impl(
2300 12x id_path, pn + (2 * cn), op);
2301 12x auto begin = s_ + impl_.offset(id_path);
2302 12x auto it = begin;
2303 12x auto end = begin + pn;
2304 92x while (it != end &&
2305 81x *it != '/')
2306 {
2307 80x ++it;
2308 }
2309 12x std::memmove(it + (2 * cn), it, end - it);
2310 12x auto src = s_ + impl_.offset(id_path) + pn;
2311 12x auto dest = s_ + impl_.offset(id_query);
2312 12x src -= end - it;
2313 12x dest -= end - it;
2314 12x pn -= end - it;
2315 do {
2316 80x --src;
2317 80x --dest;
2318 80x if (*src != ':')
2319 {
2320 56x *dest = *src;
2321 }
2322 else
2323 {
2324 24x *dest-- = 'A';
2325 24x *dest-- = '3';
2326 24x *dest = '%';
2327 }
2328 80x --pn;
2329 80x } while (pn);
2330 }
2331 }
2332
2333 //
2334 // Step 6: Update path parameters
2335 //
2336 {
2337 // The segment count is adjusted for the
2338 // "/." or "./" shield prefix, which is
2339 // not a segment of its own.
2340 1112x p = encoded_path();
2341 1112x std::size_t nseg = 0;
2342 1112x if (!p.empty())
2343 {
2344 983x nseg = std::count(
2345 1966x p.begin() + 1, p.end(), '/') + 1;
2346 }
2347 1112x impl_.nseg_ = detail::to_size_type(
2348 detail::path_segments(p, nseg));
2349 1112x impl_.decoded_[id_path] =
2350 1112x detail::to_size_type(detail::decode_bytes_unsafe(
2351 impl_.get(id_path)));
2352 }
2353 1112x return *this;
2354 1112x }
2355
2356 inline
2357 url_base&
2358 264x url_base::
2359 normalize_query()
2360 {
2361 264x op_t op(*this);
2362 264x normalize_octets_impl(
2363 id_query, unreserved_chars, op);
2364 528x return *this;
2365 264x }
2366
2367 inline
2368 url_base&
2369 261x url_base::
2370 normalize_fragment()
2371 {
2372 261x op_t op(*this);
2373 261x normalize_octets_impl(
2374 id_frag, unreserved_chars, op);
2375 522x return *this;
2376 261x }
2377
2378 inline
2379 url_base&
2380 260x url_base::
2381 normalize()
2382 {
2383 260x normalize_fragment();
2384 260x normalize_query();
2385 260x normalize_path();
2386 260x normalize_authority();
2387 260x normalize_scheme();
2388 260x return *this;
2389 }
2390
2391 //------------------------------------------------
2392 //
2393 // Implementation
2394 //
2395 //------------------------------------------------
2396
2397 inline
2398 void
2399 44237x url_base::
2400 check_invariants() const noexcept
2401 {
2402 44237x BOOST_ASSERT(
2403 impl_.len(id_scheme) == 0 ||
2404 impl_.get(id_scheme).ends_with(':'));
2405 44237x BOOST_ASSERT(
2406 impl_.len(id_user) == 0 ||
2407 impl_.get(id_user).starts_with("//"));
2408 44237x BOOST_ASSERT(
2409 impl_.len(id_pass) == 0 ||
2410 impl_.get(id_user).starts_with("//"));
2411 44237x BOOST_ASSERT(
2412 impl_.len(id_pass) == 0 ||
2413 (impl_.len(id_pass) == 1 &&
2414 impl_.get(id_pass) == "@") ||
2415 (impl_.len(id_pass) > 1 &&
2416 impl_.get(id_pass).starts_with(':') &&
2417 impl_.get(id_pass).ends_with('@')));
2418 44237x BOOST_ASSERT(
2419 impl_.len(id_user, id_path) == 0 ||
2420 impl_.get(id_user).starts_with("//"));
2421 44237x BOOST_ASSERT(impl_.decoded_[id_path] >=
2422 ((impl_.len(id_path) + 2) / 3));
2423 44237x BOOST_ASSERT(
2424 impl_.len(id_port) == 0 ||
2425 impl_.get(id_port).starts_with(':'));
2426 44237x BOOST_ASSERT(
2427 impl_.len(id_query) == 0 ||
2428 impl_.get(id_query).starts_with('?'));
2429 44237x BOOST_ASSERT(
2430 (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
2431 (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
2432 44237x BOOST_ASSERT(
2433 impl_.len(id_frag) == 0 ||
2434 impl_.get(id_frag).starts_with('#'));
2435 44237x BOOST_ASSERT(c_str()[size()] == '\0');
2436 44237x }
2437
2438 inline
2439 char*
2440 5543x url_base::
2441 resize_impl(
2442 int id,
2443 std::size_t new_size,
2444 op_t& op)
2445 {
2446 5543x return resize_impl(
2447 5543x id, id + 1, new_size, op);
2448 }
2449
2450 inline
2451 char*
2452 6278x url_base::
2453 resize_impl(
2454 int first,
2455 int last,
2456 std::size_t new_len,
2457 op_t& op)
2458 {
2459 6278x auto const n0 = impl_.len(first, last);
2460 6278x if(new_len == 0 && n0 == 0)
2461 1091x return s_ + impl_.offset(first);
2462 5187x if(new_len <= n0)
2463 2220x return shrink_impl(
2464 2220x first, last, new_len, op);
2465
2466 // growing
2467 2967x std::size_t n = new_len - n0;
2468 2967x reserve_impl(size() + n, op);
2469 auto const pos =
2470 2967x impl_.offset(last);
2471 // adjust chars
2472 2967x op.move(
2473 2967x s_ + pos + n,
2474 2967x s_ + pos,
2475 2967x impl_.offset(id_end) -
2476 pos + 1);
2477 // collapse (first, last)
2478 2967x impl_.collapse(first, last,
2479 2967x impl_.offset(last) + n);
2480 // shift (last, end) right
2481 2967x impl_.adjust_right(last, id_end, n);
2482 2967x s_[size()] = '\0';
2483 2967x return s_ + impl_.offset(first);
2484 }
2485
2486 inline
2487 char*
2488 232x url_base::
2489 shrink_impl(
2490 int id,
2491 std::size_t new_size,
2492 op_t& op)
2493 {
2494 232x return shrink_impl(
2495 232x id, id + 1, new_size, op);
2496 }
2497
2498 inline
2499 char*
2500 2452x url_base::
2501 shrink_impl(
2502 int first,
2503 int last,
2504 std::size_t new_len,
2505 op_t& op)
2506 {
2507 // shrinking
2508 2452x auto const n0 = impl_.len(first, last);
2509 2452x BOOST_ASSERT(new_len <= n0);
2510 2452x std::size_t n = n0 - new_len;
2511 auto const pos =
2512 2452x impl_.offset(last);
2513 // adjust chars
2514 2452x op.move(
2515 2452x s_ + pos - n,
2516 2452x s_ + pos,
2517 2452x impl_.offset(
2518 2452x id_end) - pos + 1);
2519 // collapse (first, last)
2520 2452x impl_.collapse(first, last,
2521 2452x impl_.offset(last) - n);
2522 // shift (last, end) left
2523 2452x impl_.adjust_left(last, id_end, n);
2524 2452x s_[size()] = '\0';
2525 2452x return s_ + impl_.offset(first);
2526 }
2527
2528 //------------------------------------------------
2529
2530 inline
2531 void
2532 529x url_base::
2533 set_scheme_impl(
2534 core::string_view s,
2535 urls::scheme id)
2536 {
2537 529x op_t op(*this, &s);
2538 529x check_invariants();
2539 577x grammar::parse(
2540 48x s, detail::scheme_rule()
2541 577x ).value(BOOST_URL_POS);
2542 481x auto const n = s.size();
2543 481x auto const p = impl_.offset(id_path);
2544
2545 // check for "./" prefix
2546 bool const has_dot =
2547 1443x [this, p]
2548 {
2549 481x if(impl_.nseg_ == 0)
2550 213x return false;
2551 268x if(first_segment().size() < 2)
2552 42x return false;
2553 226x auto const src = s_ + p;
2554 226x if(src[0] != '.')
2555 220x return false;
2556 6x if(src[1] != '/')
2557 ✗ return false;
2558 6x return true;
2559 481x }();
2560
2561 // Remove "./"
2562 481x if(has_dot)
2563 {
2564 // do this first, for
2565 // strong exception safety
2566 12x reserve_impl(
2567 6x size() + n + 1 - 2, op);
2568 6x op.move(
2569 6x s_ + p,
2570 6x s_ + p + 2,
2571 6x size() + 1 -
2572 (p + 2));
2573 6x impl_.set_size(
2574 id_path,
2575 6x impl_.len(id_path) - 2);
2576 6x impl_.decoded_[id_path] -= 2;
2577 6x s_[size()] = '\0';
2578 }
2579
2580 481x auto dest = resize_impl(
2581 id_scheme, n + 1, op);
2582 481x s.copy(dest, n);
2583 481x dest[n] = ':';
2584 481x impl_.scheme_ = id;
2585 481x check_invariants();
2586 529x }
2587
2588 inline
2589 char*
2590 399x url_base::
2591 set_user_impl(
2592 std::size_t n,
2593 op_t& op)
2594 {
2595 399x check_invariants();
2596 399x if(impl_.len(id_pass) != 0)
2597 {
2598 // keep "//"
2599 147x auto dest = resize_impl(
2600 id_user, 2 + n, op);
2601 147x check_invariants();
2602 147x return dest + 2;
2603 }
2604 // add authority
2605 bool const make_absolute =
2606 339x !is_path_absolute() &&
2607 87x !impl_.get(id_path).empty();
2608 504x auto dest = resize_impl(
2609 252x id_user, 2 + n + 1 + make_absolute, op);
2610 252x impl_.split(id_user, 2 + n);
2611 252x dest[0] = '/';
2612 252x dest[1] = '/';
2613 252x dest[2 + n] = '@';
2614 252x if (make_absolute)
2615 {
2616 7x impl_.split(id_pass, 1);
2617 7x impl_.split(id_host, 0);
2618 7x impl_.split(id_port, 0);
2619 7x dest[3 + n] = '/';
2620 7x ++impl_.decoded_[id_path];
2621 }
2622 252x check_invariants();
2623 252x return dest + 2;
2624 }
2625
2626 inline
2627 char*
2628 379x url_base::
2629 set_password_impl(
2630 std::size_t n,
2631 op_t& op)
2632 {
2633 379x check_invariants();
2634 379x if(impl_.len(id_user) != 0)
2635 {
2636 // already have authority
2637 314x auto const dest = resize_impl(
2638 id_pass, 1 + n + 1, op);
2639 314x dest[0] = ':';
2640 314x dest[n + 1] = '@';
2641 314x check_invariants();
2642 314x return dest + 1;
2643 }
2644 // add authority
2645 bool const make_absolute =
2646 106x !is_path_absolute() &&
2647 41x !impl_.get(id_path).empty();
2648 auto const dest =
2649 130x resize_impl(
2650 id_user, id_host,
2651 65x 2 + 1 + n + 1 + make_absolute, op);
2652 65x impl_.split(id_user, 2);
2653 65x dest[0] = '/';
2654 65x dest[1] = '/';
2655 65x dest[2] = ':';
2656 65x dest[2 + n + 1] = '@';
2657 65x if (make_absolute)
2658 {
2659 8x impl_.split(id_pass, 2 + n);
2660 8x impl_.split(id_host, 0);
2661 8x impl_.split(id_port, 0);
2662 8x dest[4 + n] = '/';
2663 8x ++impl_.decoded_[id_path];
2664 }
2665 65x check_invariants();
2666 65x return dest + 3;
2667 }
2668
2669 inline
2670 char*
2671 278x url_base::
2672 set_userinfo_impl(
2673 std::size_t n,
2674 op_t& op)
2675 {
2676 // "//" {dest} "@"
2677 278x check_invariants();
2678 bool const make_absolute =
2679 408x !is_path_absolute() &&
2680 130x !impl_.get(id_path).empty();
2681 556x auto dest = resize_impl(
2682 278x id_user, id_host, n + 3 + make_absolute, op);
2683 278x impl_.split(id_user, n + 2);
2684 278x dest[0] = '/';
2685 278x dest[1] = '/';
2686 278x dest[n + 2] = '@';
2687 278x if (make_absolute)
2688 {
2689 4x impl_.split(id_pass, 1);
2690 4x impl_.split(id_host, 0);
2691 4x impl_.split(id_port, 0);
2692 4x dest[3 + n] = '/';
2693 4x ++impl_.decoded_[id_path];
2694 }
2695 278x check_invariants();
2696 278x return dest + 2;
2697 }
2698
2699 inline
2700 char*
2701 742x url_base::
2702 set_host_impl(
2703 std::size_t n,
2704 op_t& op)
2705 {
2706 742x check_invariants();
2707 742x if(impl_.len(id_user) == 0)
2708 {
2709 // add authority
2710 bool make_absolute =
2711 322x !is_path_absolute() &&
2712 148x impl_.len(id_path) != 0;
2713 174x auto pn = impl_.len(id_path);
2714 348x auto dest = resize_impl(
2715 174x id_user, n + 2 + make_absolute, op);
2716 174x impl_.split(id_user, 2);
2717 174x impl_.split(id_pass, 0);
2718 174x impl_.split(id_host, n);
2719 174x impl_.split(id_port, 0);
2720 174x impl_.split(id_path, pn + make_absolute);
2721 174x if (make_absolute)
2722 {
2723 9x dest[n + 2] = '/';
2724 9x ++impl_.decoded_[id_path];
2725 }
2726 174x dest[0] = '/';
2727 174x dest[1] = '/';
2728 174x check_invariants();
2729 174x return dest + 2;
2730 }
2731 // already have authority
2732 568x auto const dest = resize_impl(
2733 id_host, n, op);
2734 568x check_invariants();
2735 568x return dest;
2736 }
2737
2738 inline
2739 char*
2740 605x url_base::
2741 set_port_impl(
2742 std::size_t n,
2743 op_t& op)
2744 {
2745 605x check_invariants();
2746 605x if(impl_.len(id_user) != 0)
2747 {
2748 // authority exists
2749 504x auto dest = resize_impl(
2750 id_port, n + 1, op);
2751 504x dest[0] = ':';
2752 504x check_invariants();
2753 504x return dest + 1;
2754 }
2755 bool make_absolute =
2756 174x !is_path_absolute() &&
2757 73x impl_.len(id_path) != 0;
2758 202x auto dest = resize_impl(
2759 101x id_user, 3 + n + make_absolute, op);
2760 101x impl_.split(id_user, 2);
2761 101x impl_.split(id_pass, 0);
2762 101x impl_.split(id_host, 0);
2763 101x dest[0] = '/';
2764 101x dest[1] = '/';
2765 101x dest[2] = ':';
2766 101x if (make_absolute)
2767 {
2768 8x impl_.split(id_port, n + 1);
2769 8x dest[n + 3] = '/';
2770 8x ++impl_.decoded_[id_path];
2771 }
2772 101x check_invariants();
2773 101x return dest + 3;
2774 }
2775
2776 inline
2777 char*
2778 598x url_base::
2779 set_path_impl(
2780 std::size_t n,
2781 op_t& op)
2782 {
2783 598x check_invariants();
2784 598x auto const dest = resize_impl(
2785 id_path, n, op);
2786 598x return dest;
2787 }
2788
2789
2790 //------------------------------------------------
2791
2792 // return the first segment of the path.
2793 // this is needed for some algorithms.
2794 inline
2795 core::string_view
2796 268x url_base::
2797 first_segment() const noexcept
2798 {
2799 268x if(impl_.nseg_ == 0)
2800 ✗ return {};
2801 268x auto const p0 = impl_.cs_ +
2802 268x impl_.offset(id_path) +
2803 268x detail::path_prefix(
2804 268x impl_.get(id_path));
2805 268x auto const end = impl_.cs_ +
2806 268x impl_.offset(id_query);
2807 268x if(impl_.nseg_ == 1)
2808 160x return core::string_view(
2809 80x p0, end - p0);
2810 188x auto p = p0;
2811 855x while(*p != '/')
2812 {
2813 667x BOOST_ASSERT(p < end);
2814 667x ++p;
2815 }
2816 188x return core::string_view(p0, p - p0);
2817 }
2818
2819 inline
2820 detail::segments_iter_impl
2821 2315x url_base::
2822 edit_segments(
2823 detail::segments_iter_impl const& it0,
2824 detail::segments_iter_impl const& it1,
2825 detail::any_segments_iter&& src,
2826 // -1 = preserve
2827 // 0 = make relative (can fail)
2828 // 1 = make absolute
2829 int absolute)
2830 {
2831 // Iterator doesn't belong to this url
2832 2315x BOOST_ASSERT(it0.ref.alias_of(impl_));
2833
2834 // Iterator doesn't belong to this url
2835 2315x BOOST_ASSERT(it1.ref.alias_of(impl_));
2836
2837 // Iterator is in the wrong order
2838 2315x BOOST_ASSERT(it0.index <= it1.index);
2839
2840 // Iterator is out of range
2841 2315x BOOST_ASSERT(it0.index <= impl_.nseg_);
2842 2315x BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2843
2844 // Iterator is out of range
2845 2315x BOOST_ASSERT(it1.index <= impl_.nseg_);
2846 2315x BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2847
2848 //------------------------------------------------
2849 //
2850 // Calculate output prefix
2851 //
2852 // 0 = ""
2853 // 1 = "/"
2854 // 2 = "./"
2855 // 3 = "/./"
2856 //
2857 2315x bool const is_abs = is_path_absolute();
2858 2315x if(has_authority())
2859 {
2860 // Check if the new
2861 // path would be empty
2862 1855x if( src.fast_nseg == 0 &&
2863 911x it0.index == 0 &&
2864 655x it1.index == impl_.nseg_)
2865 {
2866 // VFALCO we don't have
2867 // access to nchar this early
2868 //
2869 //BOOST_ASSERT(nchar == 0);
2870 627x absolute = 0;
2871 }
2872 else
2873 {
2874 // prefix "/" required
2875 1228x absolute = 1;
2876 }
2877 }
2878 460x else if(absolute < 0)
2879 {
2880 460x absolute = is_abs; // preserve
2881 }
2882 2315x auto const path_pos = impl_.offset(id_path);
2883
2884 2315x std::size_t nchar = 0;
2885 2315x std::size_t prefix = 0;
2886 2315x bool encode_colons = false;
2887 2315x bool cp_src_prefix = false;
2888 2315x if(it0.index > 0)
2889 {
2890 // first segment unchanged
2891 870x prefix = src.fast_nseg > 0;
2892 }
2893 1445x else if(src.fast_nseg > 0)
2894 {
2895 // first segment from src
2896 718x if(! src.front.empty())
2897 {
2898 640x if( src.front == "." &&
2899 7x src.fast_nseg > 1)
2900 4x if (src.s.empty())
2901 {
2902 // if front is ".", we need the extra "." in the prefix
2903 // which will maintain the invariant that segments represent
2904 // {"."}
2905 4x prefix = 2 + absolute;
2906 }
2907 else
2908 {
2909 // if the "." prefix is explicitly required from set_path
2910 // we do not include an extra "." segment
2911 ✗ prefix = absolute;
2912 ✗ cp_src_prefix = true;
2913 }
2914 629x else if(absolute)
2915 542x prefix = 1;
2916 166x else if(has_scheme() ||
2917 79x ! src.front.contains(':'))
2918 82x prefix = 0;
2919 else
2920 {
2921 5x prefix = 0;
2922 5x encode_colons = true;
2923 }
2924 }
2925 else
2926 {
2927 85x prefix = 2 + absolute;
2928 }
2929 }
2930 else
2931 {
2932 // first segment from it1
2933 727x auto const p =
2934 727x impl_.cs_ + path_pos + it1.pos;
2935 1454x switch(impl_.cs_ +
2936 727x impl_.offset(id_query) - p)
2937 {
2938 680x case 0:
2939 // points to end
2940 680x prefix = absolute;
2941 680x break;
2942 36x default:
2943 36x BOOST_ASSERT(*p == '/');
2944 36x if(p[1] != '/')
2945 {
2946 35x if(absolute)
2947 29x prefix = 1;
2948 11x else if(has_scheme() ||
2949 11x ! it1.dereference().contains(':'))
2950 5x prefix = 0;
2951 else
2952 1x prefix = 2;
2953 35x break;
2954 }
2955 // empty
2956 BOOST_FALLTHROUGH;
2957 case 1:
2958 // empty
2959 12x BOOST_ASSERT(*p == '/');
2960 12x prefix = 2 + absolute;
2961 12x break;
2962 }
2963 }
2964
2965 // append '/' to new segs
2966 // if inserting at front.
2967 2315x std::size_t const suffix =
2968 3234x it1.index == 0 &&
2969 2494x impl_.nseg_ > 0 &&
2970 179x src.fast_nseg > 0;
2971
2972 //------------------------------------------------
2973 //
2974 // Measure the number of encoded characters
2975 // of output, and the number of inserted
2976 // segments including internal separators.
2977 //
2978 2315x src.encode_colons = encode_colons;
2979 2315x std::size_t nseg = 0;
2980 2315x if(src.measure(nchar))
2981 {
2982 1279x src.encode_colons = false;
2983 for(;;)
2984 {
2985 1605x ++nseg;
2986 1605x if(! src.measure(nchar))
2987 1277x break;
2988 326x ++nchar;
2989 }
2990 }
2991
2992 2313x switch(src.fast_nseg)
2993 {
2994 1036x case 0:
2995 1036x BOOST_ASSERT(nseg == 0);
2996 1036x break;
2997 1089x case 1:
2998 1089x BOOST_ASSERT(nseg == 1);
2999 1089x break;
3000 188x case 2:
3001 188x BOOST_ASSERT(nseg >= 2);
3002 188x break;
3003 }
3004
3005 //------------------------------------------------
3006 //
3007 // Calculate [pos0, pos1) to remove
3008 //
3009 2313x auto pos0 = it0.pos;
3010 2313x if(it0.index == 0)
3011 {
3012 // patch pos for prefix
3013 1443x pos0 = 0;
3014 }
3015 2313x auto pos1 = it1.pos;
3016 2313x if(it1.index == 0)
3017 {
3018 // patch pos for prefix
3019 919x pos1 = detail::path_prefix(
3020 impl_.get(id_path));
3021 }
3022 1394x else if(
3023 1394x it0.index == 0 &&
3024 524x it1.index < impl_.nseg_ &&
3025 nseg == 0)
3026 {
3027 // Remove the slash from segment it1
3028 // if it is becoming the new first
3029 // segment.
3030 47x ++pos1;
3031 }
3032 // calc decoded size of old range
3033 auto const dn0 =
3034 2313x detail::decode_bytes_unsafe(
3035 core::string_view(
3036 2313x impl_.cs_ +
3037 2313x impl_.offset(id_path) +
3038 pos0,
3039 pos1 - pos0));
3040
3041 //------------------------------------------------
3042 //
3043 // Resize
3044 //
3045 4626x op_t op(*this, &src.s);
3046 char* dest;
3047 char const* end;
3048 {
3049 2313x auto const nremove = pos1 - pos0;
3050 // check overflow
3051 4626x if( nchar <= max_size() && (
3052 2313x prefix + suffix <=
3053 2313x max_size() - nchar))
3054 {
3055 2313x nchar = prefix + nchar + suffix;
3056 3484x if( nchar <= nremove ||
3057 1171x nchar - nremove <=
3058 1171x max_size() - size())
3059 2313x goto ok;
3060 }
3061 // too large
3062 ✗ detail::throw_length_error();
3063 2313x ok:
3064 auto const new_size =
3065 2313x size() + nchar - nremove;
3066 2313x reserve_impl(new_size, op);
3067 2313x dest = s_ + path_pos + pos0;
3068 2313x op.move(
3069 2313x dest + nchar,
3070 2313x s_ + path_pos + pos1,
3071 2313x size() - path_pos - pos1);
3072 4626x impl_.set_size(
3073 id_path,
3074 2313x impl_.len(id_path) + nchar - nremove);
3075 2313x BOOST_ASSERT(size() == new_size);
3076 2313x end = dest + nchar;
3077 2313x auto const nseg1 =
3078 2313x static_cast<std::ptrdiff_t>(impl_.nseg_) +
3079 2313x static_cast<std::ptrdiff_t>(nseg) -
3080 2313x static_cast<std::ptrdiff_t>(it1.index) +
3081 2313x static_cast<std::ptrdiff_t>(it0.index) -
3082 static_cast<std::ptrdiff_t>(cp_src_prefix);
3083 2313x BOOST_ASSERT(nseg1 >= 0);
3084 2313x impl_.nseg_ = detail::to_size_type(nseg1);
3085 2313x if(s_)
3086 2306x s_[size()] = '\0';
3087 }
3088
3089 //------------------------------------------------
3090 //
3091 // Output segments and internal separators:
3092 //
3093 // prefix [ segment [ '/' segment ] ] suffix
3094 //
3095 2313x auto const dest0 = dest;
3096 2313x switch(prefix)
3097 {
3098 60x case 3:
3099 60x *dest++ = '/';
3100 60x *dest++ = '.';
3101 60x *dest++ = '/';
3102 60x break;
3103 42x case 2:
3104 42x *dest++ = '.';
3105 BOOST_FALLTHROUGH;
3106 1193x case 1:
3107 1193x *dest++ = '/';
3108 1193x break;
3109 1060x default:
3110 1060x break;
3111 }
3112 2313x src.rewind();
3113 2313x if(nseg > 0)
3114 {
3115 1277x src.encode_colons = encode_colons;
3116 for(;;)
3117 {
3118 1603x src.copy(dest, end);
3119 1603x if(--nseg == 0)
3120 1277x break;
3121 326x *dest++ = '/';
3122 326x src.encode_colons = false;
3123 }
3124 1277x if(suffix)
3125 179x *dest++ = '/';
3126 }
3127 2313x BOOST_ASSERT(dest == dest0 + nchar);
3128
3129 // calc decoded size of new range,
3130 auto const dn =
3131 2313x detail::decode_bytes_unsafe(
3132 2313x core::string_view(dest0, dest - dest0));
3133 2313x if(dn >= dn0)
3134 1401x impl_.decoded_[id_path] +=
3135 1401x detail::to_size_type(dn - dn0);
3136 else
3137 912x impl_.decoded_[id_path] -=
3138 912x detail::to_size_type(dn0 - dn);
3139
3140 return detail::segments_iter_impl(
3141 4626x impl_, pos0, it0.index);
3142 }
3143
3144 //------------------------------------------------
3145
3146 inline
3147 auto
3148 1701x url_base::
3149 edit_params(
3150 detail::params_iter_impl const& it0,
3151 detail::params_iter_impl const& it1,
3152 detail::any_params_iter&& src) ->
3153 detail::params_iter_impl
3154 {
3155 1701x auto pos0 = impl_.offset(id_query);
3156 1701x auto pos1 = pos0 + it1.pos;
3157 1701x pos0 = pos0 + it0.pos;
3158
3159 // Iterators belong to this url
3160 1701x BOOST_ASSERT(it0.ref.alias_of(impl_));
3161 1701x BOOST_ASSERT(it1.ref.alias_of(impl_));
3162
3163 // Iterators is in the right order
3164 1701x BOOST_ASSERT(it0.index <= it1.index);
3165
3166 // Iterators are within range
3167 1701x BOOST_ASSERT(it0.index <= impl_.nparam_);
3168 1701x BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
3169 1701x BOOST_ASSERT(it1.index <= impl_.nparam_);
3170 1701x BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
3171
3172 // calc decoded size of old range,
3173 // minus one for the leading '?' which is
3174 // not counted in decoded_[id_query].
3175 // dn0 may be -1 here when the old range is
3176 // empty and the query was non-empty; the
3177 // matching subtraction on dn below cancels
3178 // that out when the delta is taken.
3179 auto dn0 =
3180 static_cast<std::ptrdiff_t>(
3181 1701x detail::decode_bytes_unsafe(
3182 core::string_view(
3183 1701x impl_.cs_ + pos0,
3184 1701x pos1 - pos0)));
3185 1701x if(impl_.len(id_query) > 0)
3186 1162x dn0 -= 1;
3187
3188 //------------------------------------------------
3189 //
3190 // Measure the number of encoded characters
3191 // of output, and the number of inserted
3192 // segments including internal separators.
3193 //
3194
3195 1701x std::size_t nchar = 0;
3196 1701x std::size_t nparam = 0;
3197 1701x if(src.measure(nchar))
3198 {
3199 1434x ++nchar; // for '?' or '&'
3200 for(;;)
3201 {
3202 1656x ++nparam;
3203 1656x if(! src.measure(nchar))
3204 1434x break;
3205 222x ++nchar; // for '&'
3206 }
3207 }
3208
3209 //------------------------------------------------
3210 //
3211 // Resize
3212 //
3213 1696x op_t op(*this, &src.s0, &src.s1);
3214 char* dest;
3215 char const* end;
3216 {
3217 1696x auto const nremove = pos1 - pos0;
3218 // check overflow
3219 3005x if( nchar > nremove &&
3220 1309x nchar - nremove >
3221 1309x max_size() - size())
3222 {
3223 // too large
3224 ✗ detail::throw_length_error();
3225 }
3226 1696x auto const nparam1 =
3227 1696x static_cast<std::ptrdiff_t>(impl_.nparam_) +
3228 1696x static_cast<std::ptrdiff_t>(nparam) -
3229 1696x static_cast<std::ptrdiff_t>(it1.index) +
3230 1696x static_cast<std::ptrdiff_t>(it0.index);
3231 1696x BOOST_ASSERT(nparam1 >= 0);
3232 1696x reserve_impl(size() + nchar - nremove, op);
3233 1696x dest = s_ + pos0;
3234 1696x end = dest + nchar;
3235 1696x if(impl_.nparam_ > 0)
3236 {
3237 // needed when we move
3238 // the beginning of the query
3239 1157x s_[impl_.offset(id_query)] = '&';
3240 }
3241 1696x op.move(
3242 1696x dest + nchar,
3243 1696x impl_.cs_ + pos1,
3244 1696x size() - pos1);
3245 3392x impl_.set_size(
3246 id_query,
3247 1696x impl_.len(id_query) +
3248 nchar - nremove);
3249 1696x impl_.nparam_ =
3250 1696x detail::to_size_type(nparam1);
3251 1696x if(nparam1 > 0)
3252 {
3253 // needed when we erase
3254 // the beginning of the query
3255 1593x s_[impl_.offset(id_query)] = '?';
3256 }
3257 1696x if(s_)
3258 1696x s_[size()] = '\0';
3259 }
3260 1696x auto const dest0 = dest;
3261
3262 //------------------------------------------------
3263 //
3264 // Output params and internal separators:
3265 //
3266 // [ '?' param ] [ '&' param ]
3267 //
3268 1696x if(nparam > 0)
3269 {
3270 1434x if(it0.index == 0)
3271 889x *dest++ = '?';
3272 else
3273 545x *dest++ = '&';
3274 1434x src.rewind();
3275 for(;;)
3276 {
3277 1656x src.copy(dest, end);
3278 1656x if(--nparam == 0)
3279 1434x break;
3280 222x *dest++ = '&';
3281 }
3282 }
3283
3284 // calc decoded size of new range; see dn0.
3285 auto dn =
3286 static_cast<std::ptrdiff_t>(
3287 1696x detail::decode_bytes_unsafe(
3288 1696x core::string_view(dest0, dest - dest0)));
3289 1696x if(impl_.len(id_query) > 0)
3290 1593x dn -= 1;
3291
3292 1696x if(dn >= dn0)
3293 1314x impl_.decoded_[id_query] +=
3294 1314x detail::to_size_type(dn - dn0);
3295 else
3296 382x impl_.decoded_[id_query] -=
3297 382x detail::to_size_type(dn0 - dn);
3298
3299 return detail::params_iter_impl(
3300 1696x impl_,
3301 1696x pos0 - impl_.offset_[id_query],
3302 3392x it0.index);
3303 1696x }
3304
3305 //------------------------------------------------
3306
3307 inline
3308 void
3309 609x url_base::
3310 decoded_to_lower_impl(int id) noexcept
3311 {
3312 609x char* it = s_ + impl_.offset(id);
3313 609x char const* const end = s_ + impl_.offset(id + 1);
3314 3660x while(it < end)
3315 {
3316 3051x if (*it != '%')
3317 {
3318 6072x *it = grammar::to_lower(
3319 3036x *it);
3320 3036x ++it;
3321 3036x continue;
3322 }
3323 15x it += 3;
3324 }
3325 609x }
3326
3327 inline
3328 void
3329 263x url_base::
3330 to_lower_impl(int id) noexcept
3331 {
3332 263x char* it = s_ + impl_.offset(id);
3333 263x char const* const end = s_ + impl_.offset(id + 1);
3334 1163x while(it < end)
3335 {
3336 1800x *it = grammar::to_lower(
3337 900x *it);
3338 900x ++it;
3339 }
3340 263x }
3341
3342 } // urls
3343 } // boost
3344
3345 #endif
3346