TLA Line data 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_DETAIL_IMPL_SEGMENTS_ITER_IMPL_HPP
12 : #define BOOST_URL_DETAIL_IMPL_SEGMENTS_ITER_IMPL_HPP
13 :
14 : #include <boost/url/detail/decode.hpp>
15 : #include <boost/url/detail/path.hpp>
16 : #include <boost/assert.hpp>
17 :
18 : namespace boost {
19 : namespace urls {
20 : namespace detail {
21 :
22 : // begin
23 : inline
24 HIT 9364 : segments_iter_impl::
25 : segments_iter_impl(
26 9364 : detail::path_ref const& ref_) noexcept
27 9364 : : ref(ref_)
28 : {
29 9364 : pos = path_prefix(ref.buffer());
30 : // begin() starts after any malleable prefix but remembers decoded chars skipped
31 9364 : decoded_prefix = pos;
32 9364 : update();
33 9364 : }
34 :
35 : // end
36 : inline
37 16416 : segments_iter_impl::
38 : segments_iter_impl(
39 : detail::path_ref const& ref_,
40 16416 : int) noexcept
41 16416 : : ref(ref_)
42 16416 : , pos(ref.size())
43 16416 : , next(ref.size())
44 16416 : , index(ref.nseg())
45 : {
46 : // end() carries the total decoded length for O(1) range math
47 16416 : decoded_prefix = ref.decoded_size();
48 16416 : }
49 :
50 : inline
51 2313 : segments_iter_impl::
52 : segments_iter_impl(
53 : url_impl const& u_,
54 : std::size_t pos_,
55 2313 : std::size_t index_) noexcept
56 2313 : : ref(u_)
57 2313 : , pos(pos_)
58 2313 : , index(index_)
59 : {
60 2313 : auto const total = ref.nseg();
61 2313 : if(index >= total)
62 : {
63 961 : pos = ref.size();
64 961 : next = ref.size();
65 961 : decoded_prefix = ref.decoded_size();
66 : // iterator equal to end: nothing to decode
67 961 : dn = 0;
68 961 : return;
69 : }
70 :
71 1352 : if(index == 0)
72 : {
73 763 : pos = path_prefix(ref.buffer());
74 : // first segment inherits the prefix size (including leading '/')
75 763 : decoded_prefix = pos;
76 763 : update();
77 763 : return;
78 : }
79 :
80 589 : BOOST_ASSERT(pos <= ref.size());
81 : // compute decoded prefix by scanning once up to the encoded offset
82 589 : decoded_prefix = detail::decode_bytes_unsafe(
83 : core::string_view(ref.data(), pos));
84 589 : if(pos != ref.size())
85 : {
86 589 : BOOST_ASSERT(
87 : ref.data()[pos] == '/');
88 589 : ++pos; // skip '/'
89 589 : update();
90 589 : --pos;
91 589 : return;
92 : }
93 :
94 MIS 0 : update();
95 : }
96 :
97 : inline
98 : void
99 HIT 12695 : segments_iter_impl::
100 : update() noexcept
101 : {
102 12695 : BOOST_ASSERT(
103 : pos == 0 ||
104 : ref.data()[pos - 1] == '/');
105 12695 : auto const end = ref.end();
106 : char const* const p0 =
107 12695 : ref.data() + pos;
108 12695 : dn = 0;
109 12695 : auto p = p0;
110 56663 : while(p != end)
111 : {
112 49262 : if(*p == '/')
113 5294 : break;
114 43968 : if(*p != '%')
115 : {
116 39695 : ++p;
117 39695 : continue;
118 : }
119 4273 : p += 3;
120 4273 : dn += 2;
121 : }
122 12695 : next = p - ref.data();
123 12695 : dn = p - p0 - dn;
124 12695 : s_ = make_pct_string_view_unsafe(
125 12695 : p0, p - p0, dn);
126 12695 : }
127 :
128 : inline
129 : void
130 10857 : segments_iter_impl::
131 : increment() noexcept
132 : {
133 10857 : BOOST_ASSERT(
134 : index != ref.nseg());
135 10857 : auto const old_index = index;
136 10857 : auto const old_dn = dn;
137 : // add decoded length of previous segment
138 10857 : decoded_prefix += old_dn;
139 10857 : if(old_index > 0)
140 : // account for the '/' separator we just crossed
141 6005 : ++decoded_prefix;
142 10857 : ++index;
143 10857 : pos = next;
144 10857 : if(index == ref.nseg())
145 4765 : return;
146 : // "/" segment
147 6092 : auto const end = ref.end();
148 6092 : auto p = ref.data() + pos;
149 6092 : BOOST_ASSERT(p != end);
150 6092 : BOOST_ASSERT(*p == '/');
151 6092 : dn = 0;
152 6092 : ++p; // skip '/'
153 6092 : auto const p0 = p;
154 31779 : while(p != end)
155 : {
156 28672 : if(*p == '/')
157 2985 : break;
158 25687 : if(*p != '%')
159 : {
160 24649 : ++p;
161 24649 : continue;
162 : }
163 1038 : p += 3;
164 1038 : dn += 2;
165 : }
166 6092 : next = p - ref.data();
167 6092 : dn = p - p0 - dn;
168 6092 : s_ = make_pct_string_view_unsafe(
169 6092 : p0, p - p0, dn);
170 : }
171 :
172 : inline
173 : void
174 1979 : segments_iter_impl::
175 : decrement() noexcept
176 : {
177 1979 : BOOST_ASSERT(index != 0);
178 1979 : auto const current_dn = dn;
179 1979 : auto const current_index = index;
180 : // remove the decoded length of the segment we're leaving
181 1979 : decoded_prefix -= current_dn;
182 1979 : if(current_index > 0 && decoded_prefix > 0)
183 : // drop the '/' separator when stepping left of it
184 1979 : --decoded_prefix;
185 1979 : --index;
186 1979 : if(index == 0)
187 : {
188 794 : pos = path_prefix(ref.buffer());
189 794 : decoded_prefix = pos;
190 794 : update();
191 794 : BOOST_ASSERT(! s_.ends_with('/'));
192 794 : return;
193 : }
194 : // scan backwards to find the '/' before
195 : // the previous segment
196 1185 : auto const begin = ref.data() +
197 1185 : path_prefix(ref.buffer());
198 1185 : auto p = ref.data() + pos;
199 1185 : BOOST_ASSERT(p != begin);
200 4738 : while(p != begin)
201 : {
202 4738 : --p;
203 4738 : if(*p == '/')
204 1185 : break;
205 : }
206 1185 : pos = p - ref.data() + 1;
207 1185 : update();
208 1185 : --pos;
209 : }
210 :
211 : } // detail
212 : } // urls
213 : } // boost
214 :
215 : #endif
|