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