include/boost/url/impl/url_base.hpp

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