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

99.1% Lines (114/115) 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 9364x segments_iter_impl::
25 segments_iter_impl(
26 9364x detail::path_ref const& ref_) noexcept
27 9364x : ref(ref_)
28 {
29 9364x pos = path_prefix(ref.buffer());
30 // begin() starts after any malleable prefix but remembers decoded chars skipped
31 9364x decoded_prefix = pos;
32 9364x update();
33 9364x }
34
35 // end
36 inline
37 16416x segments_iter_impl::
38 segments_iter_impl(
39 detail::path_ref const& ref_,
40 16416x int) noexcept
41 16416x : ref(ref_)
42 16416x , pos(ref.size())
43 16416x , next(ref.size())
44 16416x , index(ref.nseg())
45 {
46 // end() carries the total decoded length for O(1) range math
47 16416x decoded_prefix = ref.decoded_size();
48 16416x }
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 12695x segments_iter_impl::
100 update() noexcept
101 {
102 12695x BOOST_ASSERT(
103 pos == 0 ||
104 ref.data()[pos - 1] == '/');
105 12695x auto const end = ref.end();
106 char const* const p0 =
107 12695x ref.data() + pos;
108 12695x dn = 0;
109 12695x auto p = p0;
110 56663x while(p != end)
111 {
112 49262x if(*p == '/')
113 5294x break;
114 43968x if(*p != '%')
115 {
116 39695x ++p;
117 39695x continue;
118 }
119 4273x p += 3;
120 4273x dn += 2;
121 }
122 12695x next = p - ref.data();
123 12695x dn = p - p0 - dn;
124 12695x s_ = make_pct_string_view_unsafe(
125 12695x p0, p - p0, dn);
126 12695x }
127
128 inline
129 void
130 10857x segments_iter_impl::
131 increment() noexcept
132 {
133 10857x BOOST_ASSERT(
134 index != ref.nseg());
135 10857x auto const old_index = index;
136 10857x auto const old_dn = dn;
137 // add decoded length of previous segment
138 10857x decoded_prefix += old_dn;
139 10857x if(old_index > 0)
140 // account for the '/' separator we just crossed
141 6005x ++decoded_prefix;
142 10857x ++index;
143 10857x pos = next;
144 10857x if(index == ref.nseg())
145 4765x return;
146 // "/" segment
147 6092x auto const end = ref.end();
148 6092x auto p = ref.data() + pos;
149 6092x BOOST_ASSERT(p != end);
150 6092x BOOST_ASSERT(*p == '/');
151 6092x dn = 0;
152 6092x ++p; // skip '/'
153 6092x auto const p0 = p;
154 31779x while(p != end)
155 {
156 28672x if(*p == '/')
157 2985x break;
158 25687x if(*p != '%')
159 {
160 24649x ++p;
161 24649x continue;
162 }
163 1038x p += 3;
164 1038x dn += 2;
165 }
166 6092x next = p - ref.data();
167 6092x dn = p - p0 - dn;
168 6092x s_ = make_pct_string_view_unsafe(
169 6092x p0, p - p0, dn);
170 }
171
172 inline
173 void
174 1979x segments_iter_impl::
175 decrement() noexcept
176 {
177 1979x BOOST_ASSERT(index != 0);
178 1979x auto const current_dn = dn;
179 1979x auto const current_index = index;
180 // remove the decoded length of the segment we're leaving
181 1979x decoded_prefix -= current_dn;
182 1979x if(current_index > 0 && decoded_prefix > 0)
183 // drop the '/' separator when stepping left of it
184 1979x --decoded_prefix;
185 1979x --index;
186 1979x if(index == 0)
187 {
188 794x pos = path_prefix(ref.buffer());
189 794x decoded_prefix = pos;
190 794x update();
191 794x BOOST_ASSERT(! s_.ends_with('/'));
192 794x return;
193 }
194 // scan backwards to find the '/' before
195 // the previous segment
196 1185x auto const begin = ref.data() +
197 1185x path_prefix(ref.buffer());
198 1185x auto p = ref.data() + pos;
199 1185x BOOST_ASSERT(p != begin);
200 4738x while(p != begin)
201 {
202 4738x --p;
203 4738x if(*p == '/')
204 1185x break;
205 }
206 1185x pos = p - ref.data() + 1;
207 1185x update();
208 1185x --pos;
209 }
210
211 } // detail
212 } // urls
213 } // boost
214
215 #endif
216