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

10  

11  
#ifndef BOOST_URL_DETAIL_IMPL_SEGMENTS_ITER_IMPL_HPP
11  
#ifndef BOOST_URL_DETAIL_IMPL_SEGMENTS_ITER_IMPL_HPP
12  
#define BOOST_URL_DETAIL_IMPL_SEGMENTS_ITER_IMPL_HPP
12  
#define BOOST_URL_DETAIL_IMPL_SEGMENTS_ITER_IMPL_HPP
13  

13  

14  
#include <boost/url/detail/decode.hpp>
14  
#include <boost/url/detail/decode.hpp>
15  
#include <boost/url/detail/path.hpp>
15  
#include <boost/url/detail/path.hpp>
16  
#include <boost/assert.hpp>
16  
#include <boost/assert.hpp>
17  

17  

18  
namespace boost {
18  
namespace boost {
19  
namespace urls {
19  
namespace urls {
20  
namespace detail {
20  
namespace detail {
21  

21  

22  
// begin
22  
// begin
23  
inline
23  
inline
24  
segments_iter_impl::
24  
segments_iter_impl::
25  
segments_iter_impl(
25  
segments_iter_impl(
26  
    detail::path_ref const& ref_) noexcept
26  
    detail::path_ref const& ref_) noexcept
27  
    : ref(ref_)
27  
    : ref(ref_)
28  
{
28  
{
29  
    pos = path_prefix(ref.buffer());
29  
    pos = path_prefix(ref.buffer());
30  
    // begin() starts after any malleable prefix but remembers decoded chars skipped
30  
    // begin() starts after any malleable prefix but remembers decoded chars skipped
31  
    decoded_prefix = pos;
31  
    decoded_prefix = pos;
32  
    update();
32  
    update();
33  
}
33  
}
34  

34  

35  
// end
35  
// end
36  
inline
36  
inline
37  
segments_iter_impl::
37  
segments_iter_impl::
38  
segments_iter_impl(
38  
segments_iter_impl(
39  
    detail::path_ref const& ref_,
39  
    detail::path_ref const& ref_,
40  
    int) noexcept
40  
    int) noexcept
41  
    : ref(ref_)
41  
    : ref(ref_)
42  
    , pos(ref.size())
42  
    , pos(ref.size())
43  
    , next(ref.size())
43  
    , next(ref.size())
44  
    , index(ref.nseg())
44  
    , index(ref.nseg())
45  
{
45  
{
46  
    // end() carries the total decoded length for O(1) range math
46  
    // end() carries the total decoded length for O(1) range math
47  
    decoded_prefix = ref.decoded_size();
47  
    decoded_prefix = ref.decoded_size();
48  
}
48  
}
49  

49  

50  
inline
50  
inline
51  
segments_iter_impl::
51  
segments_iter_impl::
52  
segments_iter_impl(
52  
segments_iter_impl(
53  
    url_impl const& u_,
53  
    url_impl const& u_,
54  
    std::size_t pos_,
54  
    std::size_t pos_,
55  
    std::size_t index_) noexcept
55  
    std::size_t index_) noexcept
56  
    : ref(u_)
56  
    : ref(u_)
57  
    , pos(pos_)
57  
    , pos(pos_)
58  
    , index(index_)
58  
    , index(index_)
59  
{
59  
{
60  
    auto const total = ref.nseg();
60  
    auto const total = ref.nseg();
61  
    if(index >= total)
61  
    if(index >= total)
62  
    {
62  
    {
63  
        pos = ref.size();
63  
        pos = ref.size();
64  
        next = ref.size();
64  
        next = ref.size();
65  
        decoded_prefix = ref.decoded_size();
65  
        decoded_prefix = ref.decoded_size();
66  
        // iterator equal to end: nothing to decode
66  
        // iterator equal to end: nothing to decode
67  
        dn = 0;
67  
        dn = 0;
68  
        return;
68  
        return;
69  
    }
69  
    }
70  

70  

71  
    if(index == 0)
71  
    if(index == 0)
72  
    {
72  
    {
73  
        pos = path_prefix(ref.buffer());
73  
        pos = path_prefix(ref.buffer());
74  
        // first segment inherits the prefix size (including leading '/')
74  
        // first segment inherits the prefix size (including leading '/')
75  
        decoded_prefix = pos;
75  
        decoded_prefix = pos;
76  
        update();
76  
        update();
77  
        return;
77  
        return;
78  
    }
78  
    }
79  

79  

80  
    BOOST_ASSERT(pos <= ref.size());
80  
    BOOST_ASSERT(pos <= ref.size());
81  
    // compute decoded prefix by scanning once up to the encoded offset
81  
    // compute decoded prefix by scanning once up to the encoded offset
82  
    decoded_prefix = detail::decode_bytes_unsafe(
82  
    decoded_prefix = detail::decode_bytes_unsafe(
83  
        core::string_view(ref.data(), pos));
83  
        core::string_view(ref.data(), pos));
84  
    if(pos != ref.size())
84  
    if(pos != ref.size())
85  
    {
85  
    {
86  
        BOOST_ASSERT(
86  
        BOOST_ASSERT(
87  
            ref.data()[pos] == '/');
87  
            ref.data()[pos] == '/');
88  
        ++pos; // skip '/'
88  
        ++pos; // skip '/'
89  
        update();
89  
        update();
90  
        --pos;
90  
        --pos;
91  
        return;
91  
        return;
92  
    }
92  
    }
93  

93  

94  
    update();
94  
    update();
95  
}
95  
}
96  

96  

97  
inline
97  
inline
98  
void
98  
void
99  
segments_iter_impl::
99  
segments_iter_impl::
100  
update() noexcept
100  
update() noexcept
101  
{
101  
{
 
102 +
    BOOST_ASSERT(
 
103 +
        pos == 0 ||
 
104 +
        ref.data()[pos - 1] == '/');
102  
    auto const end = ref.end();
105  
    auto const end = ref.end();
103  
    char const* const p0 =
106  
    char const* const p0 =
104  
        ref.data() + pos;
107  
        ref.data() + pos;
105  
    dn = 0;
108  
    dn = 0;
106  
    auto p = p0;
109  
    auto p = p0;
107  
    while(p != end)
110  
    while(p != end)
108  
    {
111  
    {
109  
        if(*p == '/')
112  
        if(*p == '/')
110  
            break;
113  
            break;
111  
        if(*p != '%')
114  
        if(*p != '%')
112  
        {
115  
        {
113  
            ++p;
116  
            ++p;
114  
            continue;
117  
            continue;
115  
        }
118  
        }
116  
        p += 3;
119  
        p += 3;
117  
        dn += 2;
120  
        dn += 2;
118  
    }
121  
    }
119  
    next = p - ref.data();
122  
    next = p - ref.data();
120  
    dn = p - p0 - dn;
123  
    dn = p - p0 - dn;
121  
    s_ = make_pct_string_view_unsafe(
124  
    s_ = make_pct_string_view_unsafe(
122  
        p0, p - p0, dn);
125  
        p0, p - p0, dn);
123  
}
126  
}
124  

127  

125  
inline
128  
inline
126  
void
129  
void
127  
segments_iter_impl::
130  
segments_iter_impl::
128  
increment() noexcept
131  
increment() noexcept
129  
{
132  
{
130  
    BOOST_ASSERT(
133  
    BOOST_ASSERT(
131  
        index != ref.nseg());
134  
        index != ref.nseg());
132  
    auto const old_index = index;
135  
    auto const old_index = index;
133  
    auto const old_dn = dn;
136  
    auto const old_dn = dn;
134  
    // add decoded length of previous segment
137  
    // add decoded length of previous segment
135  
    decoded_prefix += old_dn;
138  
    decoded_prefix += old_dn;
136  
    if(old_index > 0)
139  
    if(old_index > 0)
137  
        // account for the '/' separator we just crossed
140  
        // account for the '/' separator we just crossed
138  
        ++decoded_prefix;
141  
        ++decoded_prefix;
139  
    ++index;
142  
    ++index;
140  
    pos = next;
143  
    pos = next;
141  
    if(index == ref.nseg())
144  
    if(index == ref.nseg())
142  
        return;
145  
        return;
143  
    // "/" segment
146  
    // "/" segment
144  
    auto const end = ref.end();
147  
    auto const end = ref.end();
145  
    auto p = ref.data() + pos;
148  
    auto p = ref.data() + pos;
146  
    BOOST_ASSERT(p != end);
149  
    BOOST_ASSERT(p != end);
147  
    BOOST_ASSERT(*p == '/');
150  
    BOOST_ASSERT(*p == '/');
148  
    dn = 0;
151  
    dn = 0;
149  
    ++p; // skip '/'
152  
    ++p; // skip '/'
150  
    auto const p0 = p;
153  
    auto const p0 = p;
151  
    while(p != end)
154  
    while(p != end)
152  
    {
155  
    {
153  
        if(*p == '/')
156  
        if(*p == '/')
154  
            break;
157  
            break;
155  
        if(*p != '%')
158  
        if(*p != '%')
156  
        {
159  
        {
157  
            ++p;
160  
            ++p;
158  
            continue;
161  
            continue;
159  
        }
162  
        }
160  
        p += 3;
163  
        p += 3;
161  
        dn += 2;
164  
        dn += 2;
162  
    }
165  
    }
163  
    next = p - ref.data();
166  
    next = p - ref.data();
164  
    dn = p - p0 - dn;
167  
    dn = p - p0 - dn;
165  
    s_ = make_pct_string_view_unsafe(
168  
    s_ = make_pct_string_view_unsafe(
166  
        p0, p - p0, dn);
169  
        p0, p - p0, dn);
167  
}
170  
}
168  

171  

169  
inline
172  
inline
170  
void
173  
void
171  
segments_iter_impl::
174  
segments_iter_impl::
172  
decrement() noexcept
175  
decrement() noexcept
173  
{
176  
{
174  
    BOOST_ASSERT(index != 0);
177  
    BOOST_ASSERT(index != 0);
175  
    auto const current_dn = dn;
178  
    auto const current_dn = dn;
176  
    auto const current_index = index;
179  
    auto const current_index = index;
177  
    // remove the decoded length of the segment we're leaving
180  
    // remove the decoded length of the segment we're leaving
178  
    decoded_prefix -= current_dn;
181  
    decoded_prefix -= current_dn;
179  
    if(current_index > 0 && decoded_prefix > 0)
182  
    if(current_index > 0 && decoded_prefix > 0)
180  
        // drop the '/' separator when stepping left of it
183  
        // drop the '/' separator when stepping left of it
181  
        --decoded_prefix;
184  
        --decoded_prefix;
182  
    --index;
185  
    --index;
183  
    if(index == 0)
186  
    if(index == 0)
184 -
        next = pos;
 
185  
    {
187  
    {
186  
        pos = path_prefix(ref.buffer());
188  
        pos = path_prefix(ref.buffer());
187  
        decoded_prefix = pos;
189  
        decoded_prefix = pos;
188 -
        s_ = core::string_view(
190 +
        update();
189 -
            ref.data() + pos,
 
190 -
            next - pos);
 
191  
        BOOST_ASSERT(! s_.ends_with('/'));
191  
        BOOST_ASSERT(! s_.ends_with('/'));
192  
        return;
192  
        return;
193  
    }
193  
    }
 
194 +
    // scan backwards to find the '/' before
 
195 +
    // the previous segment
194  
    auto const begin = ref.data() +
196  
    auto const begin = ref.data() +
195  
        path_prefix(ref.buffer());
197  
        path_prefix(ref.buffer());
196 -
    next = pos;
198 +
    auto p = ref.data() + pos;
197 -
    auto p = ref.data() + next;
 
198 -
    auto const p1 = p;
 
199 -
    dn = 0;
 
200  
    BOOST_ASSERT(p != begin);
199  
    BOOST_ASSERT(p != begin);
201  
    while(p != begin)
200  
    while(p != begin)
202  
    {
201  
    {
203  
        --p;
202  
        --p;
204 -
        {
 
205 -
            ++dn;
 
206  
        if(*p == '/')
203  
        if(*p == '/')
207 -
        }
 
208 -
        if(*p == '%')
 
209 -
            dn += 2;
 
210  
            break;
204  
            break;
211  
    }
205  
    }
212 -
    dn = p1 - p - dn;
206 +
    pos = p - ref.data() + 1;
213 -
    pos = p - ref.data();
207 +
    update();
214 -
    // keep decoded_prefix consistent with new pos
208 +
    --pos;
215 -
    // (already adjusted above)
 
216 -
    s_ = make_pct_string_view_unsafe(
 
217 -
        p + 1, p1 - p - 1, dn);
 
218  
}
209  
}
219  

210  

220  
} // detail
211  
} // detail
221  
} // urls
212  
} // urls
222  
} // boost
213  
} // boost
223  

214  

224  
#endif
215  
#endif