include/boost/url/detail/impl/segments_iter_impl.hpp

99.2% Lines (123/124) 100.0% List of functions (6/6)
segments_iter_impl.hpp
f(x) Functions (6)
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_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 9351x segments_iter_impl::
25 segments_iter_impl(
26 9351x detail::path_ref const& ref_) noexcept
27 9351x : ref(ref_)
28 {
29 9351x pos = path_prefix(ref.buffer());
30 // begin() starts after any malleable prefix but remembers decoded chars skipped
31 9351x decoded_prefix = pos;
32 9351x update();
33 9351x }
34
35 // end
36 inline
37 16403x segments_iter_impl::
38 segments_iter_impl(
39 detail::path_ref const& ref_,
40 16403x int) noexcept
41 16403x : ref(ref_)
42 16403x , pos(ref.size())
43 16403x , next(ref.size())
44 16403x , index(ref.nseg())
45 {
46 // end() carries the total decoded length for O(1) range math
47 16403x decoded_prefix = ref.decoded_size();
48 16403x }
49
50 inline
51 2313x segments_iter_impl::
52 segments_iter_impl(
53 url_impl const& u_,
54 std::size_t pos_,
55 2313x std::size_t index_) noexcept
56 2313x : ref(u_)
57 2313x , pos(pos_)
58 2313x , index(index_)
59 {
60 2313x auto const total = ref.nseg();
61 2313x if(index >= total)
62 {
63 961x pos = ref.size();
64 961x next = ref.size();
65 961x decoded_prefix = ref.decoded_size();
66 // iterator equal to end: nothing to decode
67 961x dn = 0;
68 961x return;
69 }
70
71 1352x if(index == 0)
72 {
73 763x pos = path_prefix(ref.buffer());
74 // first segment inherits the prefix size (including leading '/')
75 763x decoded_prefix = pos;
76 763x update();
77 763x return;
78 }
79
80 589x BOOST_ASSERT(pos <= ref.size());
81 // compute decoded prefix by scanning once up to the encoded offset
82 589x decoded_prefix = detail::decode_bytes_unsafe(
83 core::string_view(ref.data(), pos));
84 589x if(pos != ref.size())
85 {
86 589x BOOST_ASSERT(
87 ref.data()[pos] == '/');
88 589x ++pos; // skip '/'
89 589x update();
90 589x --pos;
91 589x return;
92 }
93
94 update();
95 }
96
97 inline
98 void
99 10703x segments_iter_impl::
100 update() noexcept
101 {
102 10703x auto const end = ref.end();
103 char const* const p0 =
104 10703x ref.data() + pos;
105 10703x dn = 0;
106 10703x auto p = p0;
107 48039x while(p != end)
108 {
109 41759x if(*p == '/')
110 4423x break;
111 37336x if(*p != '%')
112 {
113 33536x ++p;
114 33536x continue;
115 }
116 3800x p += 3;
117 3800x dn += 2;
118 }
119 10703x next = p - ref.data();
120 10703x dn = p - p0 - dn;
121 10703x s_ = make_pct_string_view_unsafe(
122 10703x p0, p - p0, dn);
123 10703x }
124
125 inline
126 void
127 10843x segments_iter_impl::
128 increment() noexcept
129 {
130 10843x BOOST_ASSERT(
131 index != ref.nseg());
132 10843x auto const old_index = index;
133 10843x auto const old_dn = dn;
134 // add decoded length of previous segment
135 10843x decoded_prefix += old_dn;
136 10843x if(old_index > 0)
137 // account for the '/' separator we just crossed
138 5998x ++decoded_prefix;
139 10843x ++index;
140 10843x pos = next;
141 10843x if(index == ref.nseg())
142 4761x return;
143 // "/" segment
144 6082x auto const end = ref.end();
145 6082x auto p = ref.data() + pos;
146 6082x BOOST_ASSERT(p != end);
147 6082x BOOST_ASSERT(*p == '/');
148 6082x dn = 0;
149 6082x ++p; // skip '/'
150 6082x auto const p0 = p;
151 31756x while(p != end)
152 {
153 28656x if(*p == '/')
154 2982x break;
155 25674x if(*p != '%')
156 {
157 24639x ++p;
158 24639x continue;
159 }
160 1035x p += 3;
161 1035x dn += 2;
162 }
163 6082x next = p - ref.data();
164 6082x dn = p - p0 - dn;
165 6082x s_ = make_pct_string_view_unsafe(
166 6082x p0, p - p0, dn);
167 }
168
169 inline
170 void
171 1957x segments_iter_impl::
172 decrement() noexcept
173 {
174 1957x BOOST_ASSERT(index != 0);
175 1957x auto const current_dn = dn;
176 1957x auto const current_index = index;
177 // remove the decoded length of the segment we're leaving
178 1957x decoded_prefix -= current_dn;
179 1957x if(current_index > 0 && decoded_prefix > 0)
180 // drop the '/' separator when stepping left of it
181 1957x --decoded_prefix;
182 1957x --index;
183 1957x if(index == 0)
184 {
185 788x next = pos;
186 788x pos = path_prefix(ref.buffer());
187 788x decoded_prefix = pos;
188 788x s_ = core::string_view(
189 788x ref.data() + pos,
190 788x next - pos);
191 788x BOOST_ASSERT(! s_.ends_with('/'));
192 788x return;
193 }
194 1169x auto const begin = ref.data() +
195 1169x path_prefix(ref.buffer());
196 1169x next = pos;
197 1169x auto p = ref.data() + next;
198 1169x auto const p1 = p;
199 1169x BOOST_ASSERT(p != begin);
200 1169x dn = 0;
201 4697x while(p != begin)
202 {
203 4697x --p;
204 4697x if(*p == '/')
205 {
206 1169x ++dn;
207 1169x break;
208 }
209 3528x if(*p == '%')
210 168x dn += 2;
211 }
212 1169x dn = p1 - p - dn;
213 1169x pos = p - ref.data();
214 // keep decoded_prefix consistent with new pos
215 // (already adjusted above)
216 1169x s_ = make_pct_string_view_unsafe(
217 1169x p + 1, p1 - p - 1, dn);
218 }
219
220 } // detail
221 } // urls
222 } // boost
223
224 #endif
225