Line data Source code
1 : //
2 : // Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/boostorg/url
8 : //
9 :
10 : #ifndef BOOST_URL_DETAIL_SEGMENTS_RANGE_HPP
11 : #define BOOST_URL_DETAIL_SEGMENTS_RANGE_HPP
12 :
13 : #include <boost/url/detail/config.hpp>
14 : #include <boost/url/detail/url_impl.hpp>
15 : #include <boost/url/segments_base.hpp>
16 : #include <boost/url/segments_encoded_base.hpp>
17 : #include <boost/core/detail/string_view.hpp>
18 : #include <boost/assert.hpp>
19 :
20 : namespace boost {
21 : namespace urls {
22 : namespace detail {
23 :
24 : struct segments_iter_access
25 : {
26 : static
27 : segments_iter_impl const&
28 40 : impl(segments_base::iterator const& it) noexcept
29 : {
30 40 : return it.it_;
31 : }
32 :
33 : static
34 : segments_iter_impl const&
35 6 : impl(segments_encoded_base::iterator const& it) noexcept
36 : {
37 6 : return it.it_;
38 : }
39 : };
40 :
41 : inline
42 : path_ref
43 23 : make_subref_from_impls(
44 : segments_iter_impl const& first,
45 : segments_iter_impl const& last) noexcept
46 : {
47 23 : BOOST_ASSERT(first.ref.alias_of(last.ref));
48 23 : path_ref const& ref = first.ref;
49 :
50 23 : std::size_t const i0 = first.index;
51 23 : std::size_t const i1 = last.index;
52 23 : BOOST_ASSERT(i0 <= i1);
53 23 : std::size_t const nseg = i1 - i0;
54 :
55 23 : bool const absolute = ref.buffer().starts_with('/');
56 :
57 : // Empty range
58 23 : if (nseg == 0)
59 : {
60 : std::size_t off0;
61 6 : if (i0 == 0)
62 : {
63 : // [begin, begin): don't include the leading '/'
64 : // for absolute, start right after the leading '/';
65 3 : if (absolute)
66 : {
67 2 : off0 = 1;
68 : }
69 : // for relative, start at the first segment character.
70 : else
71 : {
72 1 : off0 = first.pos;
73 : }
74 : }
75 : else
76 : {
77 : // [it, it) in the middle:
78 : // skip the separator before segment i0
79 3 : off0 = first.pos + 1;
80 : }
81 :
82 6 : core::string_view const sub(ref.data() + off0, 0);
83 6 : return {sub, 0, 0};
84 : }
85 :
86 : // General case: non-empty range
87 : // Start offset
88 : std::size_t off0;
89 17 : if (i0 == 0)
90 : {
91 10 : if (absolute)
92 : {
93 : // include leading '/'
94 5 : off0 = 0;
95 : }
96 : else
97 : {
98 : // relative: start at first segment
99 5 : off0 = first.pos;
100 : }
101 : }
102 : else
103 : {
104 : // include the separator preceding segment i0
105 7 : off0 = first.pos;
106 : }
107 :
108 : // End offset
109 : std::size_t off1;
110 17 : if(i1 == ref.nseg())
111 : {
112 9 : off1 = ref.size();
113 : }
114 : else
115 : {
116 : // stop before the slash preceding i1
117 8 : off1 = last.pos;
118 : }
119 :
120 17 : BOOST_ASSERT(off1 >= off0);
121 17 : core::string_view const sub(ref.data() + off0, off1 - off0);
122 :
123 : // decoded sizes reuse iterator bookkeeping instead of rescanning
124 17 : std::size_t start_dn = (i0 == 0) ? 0 : first.decoded_prefix_size();
125 17 : std::size_t const end_dn = last.decoded_prefix_size(); // already excludes segment at `last`
126 17 : BOOST_ASSERT(end_dn >= start_dn);
127 17 : std::size_t const dn_sum = end_dn - start_dn;
128 :
129 17 : return {sub, dn_sum, nseg};
130 : }
131 :
132 : template<class Iter>
133 : inline
134 : path_ref
135 23 : make_subref(Iter const& first, Iter const& last) noexcept
136 : {
137 23 : auto const& f = segments_iter_access::impl(first);
138 23 : auto const& l = segments_iter_access::impl(last);
139 23 : return make_subref_from_impls(f, l);
140 : }
141 :
142 : } // detail
143 : } // urls
144 : } // boost
145 :
146 : #endif
|