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