Skip to content

Commit 0b7fd49

Browse files
committed
feat: segments_view iterator constructors
1 parent f27306e commit 0b7fd49

File tree

9 files changed

+531
-0
lines changed

9 files changed

+531
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
//
2+
// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com)
3+
//
4+
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
//
7+
// Official repository: https://github.com/boostorg/url
8+
//
9+
10+
#ifndef BOOST_URL_DETAIL_SEGMENTS_RANGE_HPP
11+
#define BOOST_URL_DETAIL_SEGMENTS_RANGE_HPP
12+
13+
#include <boost/url/detail/config.hpp>
14+
#include <boost/url/detail/url_impl.hpp>
15+
#include <boost/url/segments_base.hpp>
16+
#include <boost/url/segments_encoded_base.hpp>
17+
#include <boost/core/detail/string_view.hpp>
18+
#include <boost/assert.hpp>
19+
20+
namespace boost {
21+
namespace urls {
22+
namespace detail {
23+
24+
struct segments_iter_access
25+
{
26+
static
27+
segments_iter_impl const&
28+
impl(segments_base::iterator const& it) noexcept
29+
{
30+
return it.it_;
31+
}
32+
33+
static
34+
segments_iter_impl const&
35+
impl(segments_encoded_base::iterator const& it) noexcept
36+
{
37+
return it.it_;
38+
}
39+
};
40+
41+
inline
42+
path_ref
43+
make_subref_from_impls(
44+
segments_iter_impl const& first,
45+
segments_iter_impl const& last) noexcept
46+
{
47+
BOOST_ASSERT(first.ref.alias_of(last.ref));
48+
path_ref const& ref = first.ref;
49+
50+
std::size_t const i0 = first.index;
51+
std::size_t const i1 = last.index;
52+
BOOST_ASSERT(i0 <= i1);
53+
std::size_t const nseg = i1 - i0;
54+
55+
bool const absolute = ref.buffer().starts_with('/');
56+
57+
// Empty range
58+
if (nseg == 0)
59+
{
60+
std::size_t off0;
61+
if (i0 == 0)
62+
{
63+
// [begin, begin): don't include the leading '/'
64+
// for absolute, start right after the leading '/';
65+
if (absolute)
66+
{
67+
off0 = 1;
68+
}
69+
// for relative, start at the first segment character.
70+
else
71+
{
72+
off0 = first.pos;
73+
}
74+
}
75+
else
76+
{
77+
// [it, it) in the middle:
78+
// skip the separator before segment i0
79+
off0 = first.pos + 1;
80+
}
81+
82+
core::string_view const sub(ref.data() + off0, 0);
83+
return {sub, 0, 0};
84+
}
85+
86+
// General case: non-empty range
87+
// Start offset
88+
std::size_t off0;
89+
bool include_leading_slash = false;
90+
if (i0 == 0)
91+
{
92+
if (absolute)
93+
{
94+
// include leading '/'
95+
off0 = 0;
96+
include_leading_slash = true;
97+
}
98+
else
99+
{
100+
// relative: start at first segment
101+
off0 = first.pos;
102+
}
103+
}
104+
else
105+
{
106+
// skip slash before segment i0
107+
off0 = first.pos + 1;
108+
}
109+
110+
// End offset
111+
std::size_t off1;
112+
if(i1 == ref.nseg())
113+
{
114+
off1 = ref.size();
115+
}
116+
else
117+
{
118+
// stop before the slash preceding i1
119+
off1 = last.pos;
120+
}
121+
122+
BOOST_ASSERT(off1 >= off0);
123+
core::string_view const sub(ref.data() + off0, off1 - off0);
124+
125+
// Decoded-length:
126+
// sum per-segment decoded lengths + internal '/' + the leading '/'.
127+
std::size_t dn_sum = 0;
128+
{
129+
// copy to iterate
130+
segments_iter_impl cur = first;
131+
for (std::size_t k = 0; k < nseg; ++k)
132+
{
133+
// per-segment decoded length
134+
dn_sum += cur.dn;
135+
cur.increment();
136+
}
137+
// internal '/'s
138+
dn_sum += (nseg - 1);
139+
// leading '/'
140+
if (include_leading_slash)
141+
{
142+
++dn_sum;
143+
}
144+
}
145+
146+
return {sub, dn_sum, nseg};
147+
}
148+
149+
template<class Iter>
150+
inline
151+
path_ref
152+
make_subref(Iter const& first, Iter const& last) noexcept
153+
{
154+
auto const& f = segments_iter_access::impl(first);
155+
auto const& l = segments_iter_access::impl(last);
156+
return make_subref_from_impls(f, l);
157+
}
158+
159+
} // detail
160+
} // urls
161+
} // boost
162+
163+
#endif

include/boost/url/impl/segments_base.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@
1717

1818
namespace boost {
1919
namespace urls {
20+
namespace detail {
21+
struct segments_iter_access;
22+
}
2023

2124
class segments_base::iterator
2225
{
2326
detail::segments_iter_impl it_;
2427

2528
friend class segments_base;
2629
friend class segments_ref;
30+
friend struct detail::segments_iter_access;
2731

2832
iterator(detail::path_ref const&) noexcept;
2933
iterator(detail::path_ref const&, int) noexcept;

include/boost/url/impl/segments_encoded_base.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
namespace boost {
1818
namespace urls {
19+
namespace detail {
20+
struct segments_iter_access;
21+
}
1922

2023
class segments_encoded_base::iterator
2124
{
@@ -24,6 +27,7 @@ class segments_encoded_base::iterator
2427
friend class url_base;
2528
friend class segments_encoded_base;
2629
friend class segments_encoded_ref;
30+
friend struct detail::segments_iter_access;
2731

2832
iterator(detail::path_ref const&) noexcept;
2933
iterator(detail::path_ref const&, int) noexcept;

include/boost/url/segments_encoded_view.hpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,53 @@ class segments_encoded_view
167167
segments_encoded_view(
168168
core::string_view s);
169169

170+
/** Constructor
171+
172+
This function creates a new @ref segments_encoded_view
173+
from a pair of iterators referring to
174+
elements of another encoded segments
175+
view. The resulting view references
176+
the same underlying character buffer
177+
as the original.
178+
179+
The caller is responsible for ensuring
180+
that the lifetime of the original buffer
181+
extends until the constructed view
182+
is no longer referenced.
183+
184+
@par Example
185+
@code
186+
segments_encoded_view ps( "/path/to/file.txt" );
187+
188+
segments_encoded_view sub(
189+
std::next(ps.begin()),
190+
ps.end());
191+
192+
// sub represents "to/file.txt"
193+
@endcode
194+
195+
@par Preconditions
196+
The iterators must be valid and belong to
197+
the same @ref segments_encoded_view.
198+
199+
@par Postconditions
200+
`sub.buffer()` references characters in the
201+
original `ps.buffer()`.
202+
203+
@par Complexity
204+
Linear in `sub.buffer()`
205+
206+
@par Exception Safety
207+
Throws nothing.
208+
209+
@param first The beginning iterator.
210+
@param last The ending iterator.
211+
*/
212+
BOOST_URL_DECL
213+
segments_encoded_view(
214+
iterator first,
215+
iterator last) noexcept;
216+
170217
/** Assignment
171218
172219
After assignment, both views

include/boost/url/segments_view.hpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,53 @@ class segments_view
165165
segments_view(
166166
core::string_view s);
167167

168+
/** Constructor
169+
170+
This function creates a new @ref segments_view
171+
from a pair of iterators referring to
172+
elements of another segments view. The
173+
resulting view references the same
174+
underlying character buffer as the
175+
original.
176+
177+
The caller is responsible for ensuring
178+
that the lifetime of the original buffer
179+
extends until the constructed view is no
180+
longer referenced.
181+
182+
@par Example
183+
@code
184+
segments_view ps( "/path/to/file.txt" );
185+
186+
segments_view sub(
187+
std::next(ps.begin()),
188+
ps.end());
189+
190+
// sub represents "to/file.txt"
191+
@endcode
192+
193+
@par Preconditions
194+
The iterators must be valid and belong to
195+
the same @ref segments_view.
196+
197+
@par Postconditions
198+
`sub.buffer()` references characters in the
199+
original `ps.buffer()`.
200+
201+
@par Complexity
202+
Linear in `sub.buffer()`
203+
204+
@par Exception Safety
205+
Throws nothing.
206+
207+
@param first The beginning iterator.
208+
@param last The ending iterator.
209+
*/
210+
BOOST_URL_DECL
211+
segments_view(
212+
iterator first,
213+
iterator last) noexcept;
214+
168215
/** Assignment
169216
170217
After assignment, both views

src/segments_encoded_view.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111

1212
#include <boost/url/detail/config.hpp>
13+
#include <boost/url/detail/segments_range.hpp>
1314
#include <boost/url/segments_encoded_view.hpp>
1415
#include <boost/url/parse_path.hpp>
1516

@@ -32,6 +33,12 @@ segments_encoded_view(
3233
{
3334
}
3435

36+
segments_encoded_view::
37+
segments_encoded_view(iterator first, iterator last) noexcept
38+
: segments_encoded_base(detail::make_subref(first, last))
39+
{
40+
}
41+
3542
segments_encoded_view::
3643
operator
3744
segments_view() const noexcept

src/segments_view.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111

1212
#include <boost/url/detail/config.hpp>
13+
#include <boost/url/detail/segments_range.hpp>
1314
#include <boost/url/segments_view.hpp>
1415
#include <boost/url/parse_path.hpp>
1516

@@ -32,6 +33,12 @@ segments_view(
3233
{
3334
}
3435

36+
segments_view::
37+
segments_view(iterator first, iterator last) noexcept
38+
: segments_base(detail::make_subref(first, last))
39+
{
40+
}
41+
3542
} // urls
3643
} // boost
3744

test/unit/segments_encoded_view.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,23 @@ struct segments_const_encoded_view_test
9595
BOOST_TEST_THROWS(segments_encoded_view("FA%"), system::system_error);
9696
}
9797

98+
// segments_encoded_view(iterator, iterator)
99+
{
100+
segments_encoded_view ps = parse_path("/a/b/c").value();
101+
auto first = std::next(ps.begin());
102+
auto last = ps.end();
103+
segments_encoded_view sub(first, last);
104+
105+
BOOST_TEST_EQ(sub.size(), 2u);
106+
BOOST_TEST(!sub.is_absolute());
107+
BOOST_TEST_EQ(sub.buffer(), "b/c");
108+
109+
auto it = sub.begin();
110+
BOOST_TEST_EQ(*it++, "b");
111+
BOOST_TEST_EQ(*it++, "c");
112+
BOOST_TEST(it == sub.end());
113+
}
114+
98115
// operator=(segments_encoded_view)
99116
{
100117
segments_encoded_view ps0("/path/to/file.txt");

0 commit comments

Comments
 (0)