1  
//
1  
//
2  
// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com)
2  
// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/boostorg/url
7  
// Official repository: https://github.com/boostorg/url
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_URL_DETAIL_SEGMENTS_RANGE_HPP
10  
#ifndef BOOST_URL_DETAIL_SEGMENTS_RANGE_HPP
11  
#define BOOST_URL_DETAIL_SEGMENTS_RANGE_HPP
11  
#define BOOST_URL_DETAIL_SEGMENTS_RANGE_HPP
12  

12  

13  
#include <boost/url/detail/config.hpp>
13  
#include <boost/url/detail/config.hpp>
14  
#include <boost/url/detail/url_impl.hpp>
14  
#include <boost/url/detail/url_impl.hpp>
15  
#include <boost/url/segments_base.hpp>
15  
#include <boost/url/segments_base.hpp>
16  
#include <boost/url/segments_encoded_base.hpp>
16  
#include <boost/url/segments_encoded_base.hpp>
17  
#include <boost/core/detail/string_view.hpp>
17  
#include <boost/core/detail/string_view.hpp>
18  
#include <boost/assert.hpp>
18  
#include <boost/assert.hpp>
19  

19  

20  
namespace boost {
20  
namespace boost {
21  
namespace urls {
21  
namespace urls {
22  
namespace detail {
22  
namespace detail {
23  

23  

24  
struct segments_iter_access
24  
struct segments_iter_access
25  
{
25  
{
26  
    static
26  
    static
27  
    segments_iter_impl const&
27  
    segments_iter_impl const&
28  
    impl(segments_base::iterator const& it) noexcept
28  
    impl(segments_base::iterator const& it) noexcept
29  
    {
29  
    {
30  
        return it.it_;
30  
        return it.it_;
31  
    }
31  
    }
32  

32  

33  
    static
33  
    static
34  
    segments_iter_impl const&
34  
    segments_iter_impl const&
35  
    impl(segments_encoded_base::iterator const& it) noexcept
35  
    impl(segments_encoded_base::iterator const& it) noexcept
36  
    {
36  
    {
37  
        return it.it_;
37  
        return it.it_;
38  
    }
38  
    }
39  
};
39  
};
40  

40  

41  
inline
41  
inline
42  
path_ref
42  
path_ref
43  
make_subref_from_impls(
43  
make_subref_from_impls(
44  
    segments_iter_impl const& first,
44  
    segments_iter_impl const& first,
45  
    segments_iter_impl const& last) noexcept
45  
    segments_iter_impl const& last) noexcept
46  
{
46  
{
47  
    BOOST_ASSERT(first.ref.alias_of(last.ref));
47  
    BOOST_ASSERT(first.ref.alias_of(last.ref));
48  
    path_ref const& ref = first.ref;
48  
    path_ref const& ref = first.ref;
49  

49  

50  
    std::size_t const i0 = first.index;
50  
    std::size_t const i0 = first.index;
51  
    std::size_t const i1 = last.index;
51  
    std::size_t const i1 = last.index;
52  
    BOOST_ASSERT(i0 <= i1);
52  
    BOOST_ASSERT(i0 <= i1);
53  
    std::size_t const nseg = i1 - i0;
53  
    std::size_t const nseg = i1 - i0;
54  

54  

55  
    bool const absolute = ref.buffer().starts_with('/');
55  
    bool const absolute = ref.buffer().starts_with('/');
56  

56  

57  
    // Empty range
57  
    // Empty range
58  
    if (nseg == 0)
58  
    if (nseg == 0)
59  
    {
59  
    {
60  
        std::size_t off0;
60  
        std::size_t off0;
61  
        if (i0 == 0)
61  
        if (i0 == 0)
62  
        {
62  
        {
63  
            // [begin, begin): don't include the leading '/'
63  
            // [begin, begin): don't include the leading '/'
64  
            // for absolute, start right after the leading '/';
64  
            // for absolute, start right after the leading '/';
65  
            if (absolute)
65  
            if (absolute)
66  
            {
66  
            {
67  
                off0 = 1;
67  
                off0 = 1;
68  
            }
68  
            }
69  
            // for relative, start at the first segment character.
69  
            // for relative, start at the first segment character.
70  
            else
70  
            else
71  
            {
71  
            {
72  
                off0 = first.pos;
72  
                off0 = first.pos;
73  
            }
73  
            }
74  
        }
74  
        }
75  
        else
75  
        else
76  
        {
76  
        {
77  
            // [it, it) in the middle:
77  
            // [it, it) in the middle:
78  
            // skip the separator before segment i0
78  
            // skip the separator before segment i0
79  
            off0 = first.pos + 1;
79  
            off0 = first.pos + 1;
80  
        }
80  
        }
81  

81  

82  
        core::string_view const sub(ref.data() + off0, 0);
82  
        core::string_view const sub(ref.data() + off0, 0);
83  
        return {sub, 0, 0};
83  
        return {sub, 0, 0};
84  
    }
84  
    }
85  

85  

86  
    // General case: non-empty range
86  
    // General case: non-empty range
87  
    // Start offset
87  
    // Start offset
88  
    std::size_t off0;
88  
    std::size_t off0;
89  
    if (i0 == 0)
89  
    if (i0 == 0)
90  
    {
90  
    {
91  
        if (absolute)
91  
        if (absolute)
92  
        {
92  
        {
93  
            // include leading '/'
93  
            // include leading '/'
94  
            off0 = 0;
94  
            off0 = 0;
95  
        }
95  
        }
96  
        else
96  
        else
97  
        {
97  
        {
98  
            // relative: start at first segment
98  
            // relative: start at first segment
99  
            off0 = first.pos;
99  
            off0 = first.pos;
100  
        }
100  
        }
101  
    }
101  
    }
102  
    else
102  
    else
103  
    {
103  
    {
104  
        // include the separator preceding segment i0
104  
        // include the separator preceding segment i0
105  
        off0 = first.pos;
105  
        off0 = first.pos;
106  
    }
106  
    }
107  

107  

108  
    // End offset
108  
    // End offset
109  
    std::size_t off1;
109  
    std::size_t off1;
110  
    if(i1 == ref.nseg())
110  
    if(i1 == ref.nseg())
111  
    {
111  
    {
112  
        off1 = ref.size();
112  
        off1 = ref.size();
113  
    }
113  
    }
114  
    else
114  
    else
115  
    {
115  
    {
116  
        // stop before the slash preceding i1
116  
        // stop before the slash preceding i1
117  
        off1 = last.pos;
117  
        off1 = last.pos;
118  
    }
118  
    }
119  

119  

120  
    BOOST_ASSERT(off1 >= off0);
120  
    BOOST_ASSERT(off1 >= off0);
121  
    core::string_view const sub(ref.data() + off0, off1 - off0);
121  
    core::string_view const sub(ref.data() + off0, off1 - off0);
122  

122  

123  
    // decoded sizes reuse iterator bookkeeping instead of rescanning
123  
    // decoded sizes reuse iterator bookkeeping instead of rescanning
124  
    std::size_t start_dn = (i0 == 0) ? 0 : first.decoded_prefix_size();
124  
    std::size_t start_dn = (i0 == 0) ? 0 : first.decoded_prefix_size();
125  
    std::size_t const end_dn = last.decoded_prefix_size(); // already excludes segment at `last`
125  
    std::size_t const end_dn = last.decoded_prefix_size(); // already excludes segment at `last`
126  
    BOOST_ASSERT(end_dn >= start_dn);
126  
    BOOST_ASSERT(end_dn >= start_dn);
127  
    std::size_t const dn_sum = end_dn - start_dn;
127  
    std::size_t const dn_sum = end_dn - start_dn;
128  

128  

129  
    return {sub, dn_sum, nseg};
129  
    return {sub, dn_sum, nseg};
130  
}
130  
}
131  

131  

132  
template<class Iter>
132  
template<class Iter>
133  
inline
133  
inline
134  
path_ref
134  
path_ref
135  
make_subref(Iter const& first, Iter const& last) noexcept
135  
make_subref(Iter const& first, Iter const& last) noexcept
136  
{
136  
{
137  
    auto const& f = segments_iter_access::impl(first);
137  
    auto const& f = segments_iter_access::impl(first);
138  
    auto const& l = segments_iter_access::impl(last);
138  
    auto const& l = segments_iter_access::impl(last);
139  
    return make_subref_from_impls(f, l);
139  
    return make_subref_from_impls(f, l);
140  
}
140  
}
141  

141  

142  
} // detail
142  
} // detail
143  
} // urls
143  
} // urls
144  
} // boost
144  
} // boost
145  

145  

146  
#endif
146  
#endif