GCC Code Coverage Report


Directory: libs/url/
File: src/detail/segments_iter_impl.cpp
Date: 2025-11-12 22:47:45
Exec Total Coverage
Lines: 123 124 99.2%
Functions: 6 6 100.0%
Branches: 38 50 76.0%

Line Branch Exec Source
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 2286 segments_iter_impl::
25 segments_iter_impl(
26 2286 detail::path_ref const& ref_) noexcept
27 2286 : ref(ref_)
28 {
29 2286 pos = path_prefix(ref.buffer());
30 // begin() starts after any malleable prefix but remembers decoded chars skipped
31 2286 decoded_prefix = pos;
32 2286 update();
33 2286 }
34
35 // end
36 1854 segments_iter_impl::
37 segments_iter_impl(
38 detail::path_ref const& ref_,
39 1854 int) noexcept
40 1854 : ref(ref_)
41 1854 , pos(ref.size())
42 1854 , next(ref.size())
43 1854 , index(ref.nseg())
44 {
45 // end() carries the total decoded length for O(1) range math
46 1854 decoded_prefix = ref.decoded_size();
47 1854 }
48
49 595 segments_iter_impl::
50 segments_iter_impl(
51 url_impl const& u_,
52 std::size_t pos_,
53 595 std::size_t index_) noexcept
54 595 : ref(u_)
55 595 , pos(pos_)
56 595 , index(index_)
57 {
58 595 auto const total = ref.nseg();
59
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 437 times.
595 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
2/2
✓ Branch 0 taken 238 times.
✓ Branch 1 taken 199 times.
437 if(index == 0)
70 {
71 238 pos = path_prefix(ref.buffer());
72 // first segment inherits the prefix size (including leading '/')
73 238 decoded_prefix = pos;
74 238 update();
75 238 return;
76 }
77
78
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 199 times.
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
1/2
✓ Branch 1 taken 199 times.
✗ Branch 2 not taken.
199 if(pos != ref.size())
83 {
84
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 199 times.
199 BOOST_ASSERT(
85 ref.data()[pos] == '/');
86 199 ++pos; // skip '/'
87 199 update();
88 199 --pos;
89 199 return;
90 }
91
92 update();
93 }
94
95 void
96 2723 segments_iter_impl::
97 update() noexcept
98 {
99 2723 auto const end = ref.end();
100 char const* const p0 =
101 2723 ref.data() + pos;
102 2723 dn = 0;
103 2723 auto p = p0;
104
2/2
✓ Branch 0 taken 9322 times.
✓ Branch 1 taken 1133 times.
10455 while(p != end)
105 {
106
2/2
✓ Branch 0 taken 1590 times.
✓ Branch 1 taken 7732 times.
9322 if(*p == '/')
107 1590 break;
108
2/2
✓ Branch 0 taken 7364 times.
✓ Branch 1 taken 368 times.
7732 if(*p != '%')
109 {
110 7364 ++p;
111 7364 continue;
112 }
113 368 p += 3;
114 368 dn += 2;
115 }
116 2723 next = p - ref.data();
117 2723 dn = p - p0 - dn;
118 2723 s_ = make_pct_string_view_unsafe(
119 2723 p0, p - p0, dn);
120 2723 }
121
122 void
123 2775 segments_iter_impl::
124 increment() noexcept
125 {
126
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2775 times.
2775 BOOST_ASSERT(
127 index != ref.nseg());
128 2775 auto const old_index = index;
129 2775 auto const old_dn = dn;
130 // add decoded length of previous segment
131 2775 decoded_prefix += old_dn;
132
2/2
✓ Branch 0 taken 1579 times.
✓ Branch 1 taken 1196 times.
2775 if(old_index > 0)
133 // account for the '/' separator we just crossed
134 1579 ++decoded_prefix;
135 2775 ++index;
136 2775 pos = next;
137
2/2
✓ Branch 1 taken 1135 times.
✓ Branch 2 taken 1640 times.
2775 if(index == ref.nseg())
138 1135 return;
139 // "/" segment
140 1640 auto const end = ref.end();
141 1640 auto p = ref.data() + pos;
142
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1640 times.
1640 BOOST_ASSERT(p != end);
143
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1640 times.
1640 BOOST_ASSERT(*p == '/');
144 1640 dn = 0;
145 1640 ++p; // skip '/'
146 1640 auto const p0 = p;
147
2/2
✓ Branch 0 taken 6493 times.
✓ Branch 1 taken 692 times.
7185 while(p != end)
148 {
149
2/2
✓ Branch 0 taken 948 times.
✓ Branch 1 taken 5545 times.
6493 if(*p == '/')
150 948 break;
151
2/2
✓ Branch 0 taken 5433 times.
✓ Branch 1 taken 112 times.
5545 if(*p != '%')
152 {
153 5433 ++p;
154 5433 continue;
155 }
156 112 p += 3;
157 112 dn += 2;
158 }
159 1640 next = p - ref.data();
160 1640 dn = p - p0 - dn;
161 1640 s_ = make_pct_string_view_unsafe(
162 1640 p0, p - p0, dn);
163 }
164
165 void
166 1584 segments_iter_impl::
167 decrement() noexcept
168 {
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1584 times.
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
2/4
✓ Branch 0 taken 1584 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1584 times.
✗ Branch 3 not taken.
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
2/2
✓ Branch 0 taken 549 times.
✓ Branch 1 taken 1035 times.
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
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 549 times.
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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1035 times.
1035 BOOST_ASSERT(p != begin);
195 1035 dn = 0;
196
1/2
✓ Branch 0 taken 3188 times.
✗ Branch 1 not taken.
3188 while(p != begin)
197 {
198 3188 --p;
199
2/2
✓ Branch 0 taken 1035 times.
✓ Branch 1 taken 2153 times.
3188 if(*p == '/')
200 {
201 1035 ++dn;
202 1035 break;
203 }
204
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 2125 times.
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
218