include/boost/url/impl/url_base.hpp

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